diff --git a/Gemfile b/Gemfile
index 679e0a5b2..976184793 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,140 +1,140 @@
-source 'http://ruby.taobao.org'
-#source 'http://ruby.sdutlinux.org/'
-
-unless RUBY_PLATFORM =~ /w32/
- # unix-like only
- gem 'iconv'
-end
-
-gem 'rubyzip'
-gem 'delayed_job_active_record'#, :group => :production
-gem 'daemons'
-gem 'grape', '~> 0.9.0'
-gem 'grape-entity'
-gem 'seems_rateable', '~> 1.0.13'
-gem "rails", "3.2.13"
-gem "jquery-rails", "~> 2.0.2"
-gem "i18n", "~> 0.6.0"
-gem 'coderay', '~> 1.1.0'
-gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
-gem "builder", "3.0.0"
-gem 'acts-as-taggable-on', '2.4.1'
-gem 'spreadsheet'
-gem 'ruby-ole'
-#gem 'email_verifier', path: 'lib/email_verifier'
-gem 'rufus-scheduler'
-#gem 'dalli', path: 'lib/dalli-2.7.2'
-gem 'rails_kindeditor',path:'lib/rails_kindeditor'
-group :development do
- gem 'grape-swagger'
- #gem 'grape-swagger-ui', git: 'https://github.com/guange2015/grape-swagger-ui.git'
- gem 'puma' if RbConfig::CONFIG['host_os'] =~ /linux/
- gem 'pry-rails'
- if RUBY_VERSION >= '2.0.0'
- gem 'pry-byebug'
- else
- # gem 'pry-debugger'
- end
- gem 'pry-stack_explorer'
- gem 'better_errors', '~> 1.1.0'
- gem 'rack-mini-profiler', '~> 0.9.3'
-end
-
-group :test do
- gem "shoulda", "~> 3.5.0"
- gem "mocha", "~> 1.1.0"
- gem 'capybara', '~> 2.4.1'
- gem 'nokogiri', '~> 1.6.3'
- gem 'factory_girl', '~> 4.4.0'
- gem 'selenium-webdriver', '~> 2.42.0'
-
- gem "faker"
- # platforms :mri, :mingw do
- # group :rmagick do
- # # RMagick 2 supports ruby 1.9
- # # RMagick 1 would be fine for ruby 1.8 but Bundler does not support
- # # different requirements for the same gem on different platforms
- # gem "rmagick", ">= 2.0.0"
- # end
- #end
-end
-
-# Gems used only for assets and not required
-# in production environments by default.
-group :assets do
- gem 'sass-rails', '~> 3.2.3'
- gem 'coffee-rails', '~> 3.2.1'
-
- # See https://github.com/sstephenson/execjs#readme for more supported runtimes
- gem 'therubyracer', :platforms => :ruby
-
- gem 'uglifier', '>= 1.0.3'
-end
-
-# Optional gem for LDAP authentication
-group :ldap do
- gem "net-ldap", "~> 0.3.1"
-end
-
-
-# Optional gem for OpenID authentication
-group :openid do
- gem "ruby-openid", "~> 2.1.4", :require => "openid"
- gem "rack-openid"
-end
-
-# Optional gem for exporting the gantt to a PNG file, not supported with jruby
-platforms :jruby do
- # jruby-openssl is bundled with JRuby 1.7.0
- gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0'
- gem "activerecord-jdbc-adapter", "1.2.5"
-end
-
-# Include database gems for the adapters found in the database
-# configuration file
-require 'erb'
-require 'yaml'
-database_file = File.join(File.dirname(__FILE__), "config/database.yml")
-if File.exist?(database_file)
- database_config = YAML::load(ERB.new(IO.read(database_file)).result)
- adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
- if adapters.any?
- adapters.each do |adapter|
- case adapter
- when 'mysql2'
- gem "mysql2", "= 0.3.18", :platforms => [:mri, :mingw]
- gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
- when 'mysql'
- gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
- gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
- when /postgresql/
- gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
- gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
- when /sqlite3/
- gem "sqlite3", :platforms => [:mri, :mingw]
- gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
- when /sqlserver/
- gem "tiny_tds", "~> 0.5.1", :platforms => [:mri, :mingw]
- gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
- else
- warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
- end
- end
- else
- warn("No adapter found in config/database.yml, please configure it first")
- end
-else
- warn("Please configure your config/database.yml first")
-end
-
-local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
-if File.exists?(local_gemfile)
- puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
- instance_eval File.read(local_gemfile)
-end
-
-# Load plugins' Gemfiles
-Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
- puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
- instance_eval File.read(file)
-end
+source 'http://ruby.taobao.org'
+#source 'http://ruby.sdutlinux.org/'
+
+unless RUBY_PLATFORM =~ /w32/
+ # unix-like only
+ gem 'iconv'
+end
+
+gem 'rubyzip'
+gem 'delayed_job_active_record'#, :group => :production
+gem 'daemons'
+gem 'grape', '~> 0.9.0'
+gem 'grape-entity'
+gem 'seems_rateable', '~> 1.0.13'
+gem "rails", "3.2.13"
+gem "jquery-rails", "~> 2.0.2"
+gem "i18n", "~> 0.6.0"
+gem 'coderay', '~> 1.1.0'
+gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
+gem "builder", "3.0.0"
+gem 'acts-as-taggable-on', '2.4.1'
+gem 'spreadsheet'
+gem 'ruby-ole'
+#gem 'email_verifier', path: 'lib/email_verifier'
+gem 'rufus-scheduler'
+#gem 'dalli', path: 'lib/dalli-2.7.2'
+gem 'rails_kindeditor',path:'lib/rails_kindeditor'
+group :development do
+ gem 'grape-swagger'
+ #gem 'grape-swagger-ui', git: 'https://github.com/guange2015/grape-swagger-ui.git'
+ gem 'puma' if RbConfig::CONFIG['host_os'] =~ /linux/
+ gem 'pry-rails'
+ if RUBY_VERSION >= '2.0.0'
+ gem 'pry-byebug'
+ else
+ # gem 'pry-debugger'
+ end
+ gem 'pry-stack_explorer'
+ gem 'better_errors', '~> 1.1.0'
+ gem 'rack-mini-profiler', '~> 0.9.3'
+end
+
+group :test do
+ gem "shoulda", "~> 3.5.0"
+ gem "mocha", "~> 1.1.0"
+ gem 'capybara', '~> 2.4.1'
+ gem 'nokogiri', '~> 1.6.3'
+ gem 'factory_girl', '~> 4.4.0'
+ gem 'selenium-webdriver', '~> 2.42.0'
+
+ gem "faker"
+ # platforms :mri, :mingw do
+ # group :rmagick do
+ # # RMagick 2 supports ruby 1.9
+ # # RMagick 1 would be fine for ruby 1.8 but Bundler does not support
+ # # different requirements for the same gem on different platforms
+ # gem "rmagick", ">= 2.0.0"
+ # end
+ #end
+end
+
+# Gems used only for assets and not required
+# in production environments by default.
+group :assets do
+ gem 'sass-rails', '~> 3.2.3'
+ gem 'coffee-rails', '~> 3.2.1'
+
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
+ gem 'therubyracer', :platforms => :ruby
+
+ gem 'uglifier', '>= 1.0.3'
+end
+
+# Optional gem for LDAP authentication
+group :ldap do
+ gem "net-ldap", "~> 0.3.1"
+end
+
+
+# Optional gem for OpenID authentication
+group :openid do
+ gem "ruby-openid", "~> 2.1.4", :require => "openid"
+ gem "rack-openid"
+end
+
+# Optional gem for exporting the gantt to a PNG file, not supported with jruby
+platforms :jruby do
+ # jruby-openssl is bundled with JRuby 1.7.0
+ gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0'
+ gem "activerecord-jdbc-adapter", "1.2.5"
+end
+
+# Include database gems for the adapters found in the database
+# configuration file
+require 'erb'
+require 'yaml'
+database_file = File.join(File.dirname(__FILE__), "config/database.yml")
+if File.exist?(database_file)
+ database_config = YAML::load(ERB.new(IO.read(database_file)).result)
+ adapters = database_config.values.map {|c| c['adapter']}.compact.uniq
+ if adapters.any?
+ adapters.each do |adapter|
+ case adapter
+ when 'mysql2'
+ gem "mysql2", "= 0.3.18", :platforms => [:mri, :mingw]
+ gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
+ when 'mysql'
+ gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw]
+ gem "activerecord-jdbcmysql-adapter", :platforms => :jruby
+ when /postgresql/
+ gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw]
+ gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
+ when /sqlite3/
+ gem "sqlite3", :platforms => [:mri, :mingw]
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
+ when /sqlserver/
+ gem "tiny_tds", "~> 0.5.1", :platforms => [:mri, :mingw]
+ gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
+ else
+ warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems")
+ end
+ end
+ else
+ warn("No adapter found in config/database.yml, please configure it first")
+ end
+else
+ warn("Please configure your config/database.yml first")
+end
+
+local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
+if File.exists?(local_gemfile)
+ puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
+ instance_eval File.read(local_gemfile)
+end
+
+# Load plugins' Gemfiles
+Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file|
+ puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v`
+ instance_eval File.read(file)
+end
diff --git a/app/api/mobile/api.rb b/app/api/mobile/api.rb
index 4724b0bc0..a59b01776 100644
--- a/app/api/mobile/api.rb
+++ b/app/api/mobile/api.rb
@@ -1,49 +1,49 @@
-module Mobile
- require_relative 'middleware/error_handler'
- require_relative 'apis/auth'
- require_relative 'apis/users'
- require_relative 'apis/courses'
- require_relative 'apis/watches'
- require_relative 'apis/upgrade'
- require_relative 'apis/homeworks'
- require_relative 'apis/comments'
- class API < Grape::API
- version 'v1', using: :path
- format :json
- content_type :json, "application/json;charset=UTF-8"
- use Mobile::Middleware::ErrorHandler
-
- helpers do
- def logger
- API.logger
- end
-
- def authenticate!
- raise('Unauthorized. Invalid or expired token.') unless current_user
- end
-
- def current_user
- token = ApiKey.where(access_token: params[:token]).first
- if token && !token.expired?
- @current_user = User.find(token.user_id)
- else
- nil
- end
- end
- end
-
- mount Apis::Auth
- mount Apis::Users
- mount Apis::Courses
- mount Apis::Watches
- mount Apis::Upgrade
- mount Apis::Homeworks
- mount Apis::Comments
-
- #add_swagger_documentation ({api_version: 'v1', base_path: 'http://u06.shellinfo.cn/trustie/api'})
- #add_swagger_documentation ({api_version: 'v1', base_path: '/api'}) if Rails.env.development?
-
- end
-end
-
-
+module Mobile
+ require_relative 'middleware/error_handler'
+ require_relative 'apis/auth'
+ require_relative 'apis/users'
+ require_relative 'apis/courses'
+ require_relative 'apis/watches'
+ require_relative 'apis/upgrade'
+ require_relative 'apis/homeworks'
+ require_relative 'apis/comments'
+ class API < Grape::API
+ version 'v1', using: :path
+ format :json
+ content_type :json, "application/json;charset=UTF-8"
+ use Mobile::Middleware::ErrorHandler
+
+ helpers do
+ def logger
+ API.logger
+ end
+
+ def authenticate!
+ raise('Unauthorized. Invalid or expired token.') unless current_user
+ end
+
+ def current_user
+ token = ApiKey.where(access_token: params[:token]).first
+ if token && !token.expired?
+ @current_user = User.find(token.user_id)
+ else
+ nil
+ end
+ end
+ end
+
+ mount Apis::Auth
+ mount Apis::Users
+ mount Apis::Courses
+ mount Apis::Watches
+ mount Apis::Upgrade
+ mount Apis::Homeworks
+ mount Apis::Comments
+
+ #add_swagger_documentation ({api_version: 'v1', base_path: 'http://u06.shellinfo.cn/trustie/api'})
+ #add_swagger_documentation ({api_version: 'v1', base_path: '/api'}) if Rails.env.development?
+
+ end
+end
+
+
diff --git a/app/controllers/applied_project_controller.rb b/app/controllers/applied_project_controller.rb
index 8e70ed32c..b9824976b 100644
--- a/app/controllers/applied_project_controller.rb
+++ b/app/controllers/applied_project_controller.rb
@@ -1,59 +1,59 @@
-class AppliedProjectController < ApplicationController
-
- #申请加入项目
- def applied_join_project
- @user_id = params[:user_id]
- @project = Project.find_by_id(params[:project_id])
- if params[:project_join]
- if @project
- user = User.find @user_id
- if user.member_of?(@project)
- @status = 3
- else
- @applieds = AppliedProject.where("user_id = ? and project_id = ?", params[:user_id],params[:project_id])
- if @applieds.count == 0
- appliedproject = AppliedProject.create(:user_id => params[:user_id], :project_id => params[:project_id])
- Mailer.run.applied_project(appliedproject)
- @status = 2
- else
- @status = 1
- end
- end
- else
- @status = 0
- end
- respond_to do |format|
- format.js
- end
- return
- end
-
- @applieds = AppliedProject.where("user_id = ? and project_id = ?", params[:user_id],params[:project_id])
- if @applieds.count == 0
- appliedproject = AppliedProject.create(:user_id => params[:user_id], :project_id => params[:project_id])
- Mailer.run.applied_project(appliedproject)
- end
-
- #redirect_to project_path(params[:project_id])
- #redirect_to_referer_or {render :text => ( 'applied success.'), :layout => true}
- respond_to do |format|
- format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
- format.js { render :partial => 'set_applied'}
- end
- end
-
- #取消申请
- def unapplied_join_project
- @project = Project.find(params[:project_id])
- #@applied = AppliedProject.find(params[:id])
- #@applied.destroy
-
- AppliedProject.deleteappiled(params[:user_id], params[:project_id])
-
- respond_to do |format|
- format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
- format.js { render :partial => 'set_applied' }
- end
- end
-
-end
+class AppliedProjectController < ApplicationController
+
+ #申请加入项目
+ def applied_join_project
+ @user_id = params[:user_id]
+ @project = Project.find_by_id(params[:project_id])
+ if params[:project_join]
+ if @project
+ user = User.find @user_id
+ if user.member_of?(@project)
+ @status = 3
+ else
+ @applieds = AppliedProject.where("user_id = ? and project_id = ?", params[:user_id],params[:project_id])
+ if @applieds.count == 0
+ appliedproject = AppliedProject.create(:user_id => params[:user_id], :project_id => params[:project_id])
+ Mailer.run.applied_project(appliedproject)
+ @status = 2
+ else
+ @status = 1
+ end
+ end
+ else
+ @status = 0
+ end
+ respond_to do |format|
+ format.js
+ end
+ return
+ end
+
+ @applieds = AppliedProject.where("user_id = ? and project_id = ?", params[:user_id],params[:project_id])
+ if @applieds.count == 0
+ appliedproject = AppliedProject.create(:user_id => params[:user_id], :project_id => params[:project_id])
+ Mailer.run.applied_project(appliedproject)
+ end
+
+ #redirect_to project_path(params[:project_id])
+ #redirect_to_referer_or {render :text => ( 'applied success.'), :layout => true}
+ respond_to do |format|
+ format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
+ format.js { render :partial => 'set_applied'}
+ end
+ end
+
+ #取消申请
+ def unapplied_join_project
+ @project = Project.find(params[:project_id])
+ #@applied = AppliedProject.find(params[:id])
+ #@applied.destroy
+
+ AppliedProject.deleteappiled(params[:user_id], params[:project_id])
+
+ respond_to do |format|
+ format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
+ format.js { render :partial => 'set_applied' }
+ end
+ end
+
+end
diff --git a/app/controllers/bids_controller.rb b/app/controllers/bids_controller.rb
index d38846ea0..b1cca68ed 100644
--- a/app/controllers/bids_controller.rb
+++ b/app/controllers/bids_controller.rb
@@ -506,7 +506,8 @@ class BidsController < ApplicationController
end
@cur_page = params[:page] || 1
- @homework_list = paginateHelper all_homework_list,10
+ # @homework_list = paginateHelper all_homework_list,10
+ @homework_list = all_homework_list
@jours_count = @bid.journals_for_messages.where('m_parent_id IS NULL').count
if params[:student_id].present?
@temp = []
@@ -792,7 +793,7 @@ class BidsController < ApplicationController
@bid.is_evaluation = params[:bid][:is_evaluation]
@bid.proportion = params[:bid][:proportion]
@bid.evaluation_num = params[:bid][:evaluation_num]
- @bid.open_anonymous_evaluation = params[:bid][:open_anonymous_evaluation]
+ params[:bid][:open_anonymous_evaluation] ? @bid.open_anonymous_evaluation = 1 : @bid.open_anonymous_evaluation = 0
@bid.reward_type = 3
# @bid.budget = params[:bid][:budget]
@bid.deadline = params[:bid][:deadline]
diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb
index c0e99d546..a1e547a84 100644
--- a/app/controllers/courses_controller.rb
+++ b/app/controllers/courses_controller.rb
@@ -191,8 +191,9 @@ class CoursesController < ApplicationController
results = searchmember_by_name(student_homework_score(@group.id,0,0,"desc"), q)
end
@is_remote = true
- @result_count = results.count
- @results = paginateHelper results, 10
+ #@result_count = results.count
+ #@results = paginateHelper results, 10
+ @results = results
@search_name = q
end
@@ -315,13 +316,15 @@ class CoursesController < ApplicationController
when '1'
@subPage_title = l :label_teacher_list
@all_members = searchTeacherAndAssistant(@course)
- @members = paginateHelper @all_members, 10
+ #@members = paginateHelper @all_members, 10
+ @members = @all_members
when '2'
@subPage_title = l :label_student_list
page = params[:page].nil? ? 0 : (params['page'].to_i - 1)
@all_members = student_homework_score(0,page, 10,"desc")
# @all_members = @course.members
- @members = paginateHelper_for_members @all_members, 10
+ # @members = paginateHelper_for_members @all_members, 10
+ @members = @all_members
end
respond_to do |format|
if params[:page]
@@ -705,7 +708,7 @@ class CoursesController < ApplicationController
#"show_course_journals_for_messages" => true,
"show_bids" => true,
"show_homeworks" => true,
- #"show_polls" => true
+ "show_polls" => true
}
@date_to ||= Date.today + 1
@date_from = (@date_to - @days) > @course.created_at.to_date ? (@date_to - @days) : @course.created_at.to_date
@@ -877,7 +880,7 @@ class CoursesController < ApplicationController
students_for_courses.course_id = #{@course.id} and members.user_id = students_for_courses.student_id AND
members.user_id NOT IN (SELECT homework_attaches.user_id FROM homework_attaches WHERE homework_attaches.bid_id in (SELECT bid_id FROM homework_for_courses WHERE course_id = #{@course.id} )
)
- GROUP BY members.user_id ORDER BY score #{score_sort_by} limit #{start_from}, #{nums}"
+ GROUP BY members.user_id ORDER BY score #{score_sort_by} " #limit #{start_from}, #{nums}"
end
else
diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb
index 8b1c8ba32..69e7105aa 100644
--- a/app/controllers/files_controller.rb
+++ b/app/controllers/files_controller.rb
@@ -145,10 +145,14 @@ class FilesController < ApplicationController
ids += version.id.to_s
end
end
- resultSet = Attachment.where("((attachments.container_type = 'Project' And attachments.container_id = '#{project.id}') OR (container_type = 'Version' AND container_id IN (#{ids}))) AND filename LIKE :like ", like: "%#{keywords}%").
- reorder(sort)
- #resultSet = Attachment.find_by_sql("SELECT `attachments`.* FROM `attachments` LEFT OUTER JOIN `homework_attaches` ON `attachments`.container_type = 'HomeworkAttach' AND `attachments`.container_id = `homework_attaches`.id LEFT OUTER JOIN `homework_for_courses` ON `homework_attaches`.bid_id = `homework_for_courses`.bid_id LEFT OUTER JOIN `homework_for_courses` AS H_C ON `attachments`.container_type = 'Bid' AND `attachments`.container_id = H_C.bid_id WHERE (`homework_for_courses`.course_id = 117 OR H_C.course_id = 117 OR (`attachments`.container_type = 'Course' AND `attachments`.container_id = 117)) AND `attachments`.filename LIKE '%#{keywords}%'").reorder("created_on DESC")
- end
+ if ids.blank?
+ resultSet = Attachment.where("attachments.container_type = 'Project' And attachments.container_id = '#{project.id}' AND filename LIKE :like ", like: "%#{keywords}%").
+ reorder(sort)
+ else
+ resultSet = Attachment.where("((attachments.container_type = 'Project' And attachments.container_id = '#{project.id}') OR (container_type = 'Version' AND container_id IN (#{ids}))) AND filename LIKE :like ", like: "%#{keywords}%").
+ reorder(sort)
+ end
+ end
def find_public_attache keywords,sort = ""
# StoresController#search 将每条文件都查出来,再次进行判断过滤。---> resultSet.to_a.map
diff --git a/app/controllers/homework_attach_controller.rb b/app/controllers/homework_attach_controller.rb
index a589ce144..ee95277ce 100644
--- a/app/controllers/homework_attach_controller.rb
+++ b/app/controllers/homework_attach_controller.rb
@@ -59,7 +59,8 @@ class HomeworkAttachController < ApplicationController
all_homework_list = search_homework_member(all_homework_list,@search_name.to_s.downcase) if @search_name
@cur_page = params[:page] || 1
@cur_type = 2
- @homework_list = paginateHelper all_homework_list,10
+ # @homework_list = paginateHelper all_homework_list,10
+ @homework_list = all_homework_list
@direction = direction == 'asc'? 'desc' : 'asc'
respond_to do |format|
format.js
@@ -93,7 +94,8 @@ class HomeworkAttachController < ApplicationController
all_homework_list = search_homework_member(all_homework_list,@search_name.to_s.downcase) if @search_name
@cur_page = params[:page] || 1
@cur_type = 3
- @homework_list = paginateHelper all_homework_list,10
+ # @homework_list = paginateHelper all_homework_list,10
+ @homework_list = all_homework_list
@direction = direction == 'asc'? 'desc' : 'asc'
respond_to do |format|
format.js
@@ -110,7 +112,8 @@ class HomeworkAttachController < ApplicationController
all_homework_list = get_student_batch_homework_list @bid,User.current
@cur_page = params[:page] || 1
@cur_type = 4
- @homework_list = paginateHelper all_homework_list,10
+ # @homework_list = paginateHelper all_homework_list,10
+ @homework_list = all_homework_list
respond_to do |format|
format.js
end
@@ -134,7 +137,8 @@ class HomeworkAttachController < ApplicationController
WHERE homework_attaches.bid_id = #{@bid.id} AND homework_users.user_id = #{User.current.id}")
end
@cur_page = params[:page] || 1
- @homework_list = paginateHelper all_homework_list,10
+ # @homework_list = paginateHelper all_homework_list,10
+ @homework_list = all_homework_list
respond_to do |format|
format.js
end
@@ -612,7 +616,8 @@ class HomeworkAttachController < ApplicationController
ORDER BY #{order_by}) AS table1
WHERE table1.t_score IS NULL OR table1.t_score = 0 ")
@all_homework_list = search_homework_member(@all_homework_list,@search_name.to_s.downcase) if @search_name
- @homework_list = paginateHelper @all_homework_list,10
+ # @homework_list = paginateHelper @all_homework_list,10
+ @homework_list = @all_homework_list
end
#获取指定作业的所有成员
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 790718e5d..80be8c5d5 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -95,6 +95,7 @@ class IssuesController < ApplicationController
format.api {
Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
}
+ # format.json { render :json => @issues.map { |issue| issue.to_json}} #:json => @issues.map { |issue| issue.to_json}
format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
format.csv { send_data(query_to_csv(@issues, @query, params), :type => 'text/csv; header=present', :filename => 'issues.csv') }
format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'issues.pdf') }
diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb
index fc243741a..6271a6832 100644
--- a/app/controllers/members_controller.rb
+++ b/app/controllers/members_controller.rb
@@ -60,8 +60,12 @@ class MembersController < ApplicationController
user_ids.each do |user_id|
AppliedProject.deleteappiled(user_id, @project.id)
end
+ @succes_message = "拒绝成功"
end
end
+ respond_to do |format|
+ format.js
+ end
else
#modify by nwb
#更改课程成员逻辑
@@ -117,9 +121,14 @@ class MembersController < ApplicationController
format.html { redirect_to invite_members_project_url(@project) }
end
else
+ unless members.present? && members.all? {|m| m.valid? }
+ @project_error_message = members.empty? ? l(:label_user_role_null) :members.collect {|m| m.errors.full_messages}.flatten.uniq.join(', ')
+ else
+ @succes_message = "添加成功"
+ end
respond_to do |format|
format.html { redirect_to_settings_in_projects }
- format.js { @members = members; @applied_members = applied_members; }
+ format.js
format.api {
@member = members.first
if @member.valid?
@@ -184,6 +193,8 @@ class MembersController < ApplicationController
end # end of params[:refusal_button]
+
+
end
def update
diff --git a/app/controllers/poll_controller.rb b/app/controllers/poll_controller.rb
index b897d039a..32ec3dad2 100644
--- a/app/controllers/poll_controller.rb
+++ b/app/controllers/poll_controller.rb
@@ -29,7 +29,7 @@ class PollController < ApplicationController
end
#已提交问卷的用户不能再访问该界面
if has_commit_poll?(@poll.id,User.current.id) && (!User.current.admin?)
- render_403
+ redirect_to poll_index_url(:polls_type => "Course", :polls_group_id => @course.id)
else
@can_edit_poll = (!has_commit_poll?(@poll.id,User.current.id)) || User.current.admin?
@percent = get_percent(@poll,User.current)
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8ed9fcfef..5e67e0a2c 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -27,15 +27,7 @@ class ProjectsController < ApplicationController
menu_item :feedback, :only => :feedback
menu_item :share, :only => :share
- before_filter :find_project, :except => [ :index, :search,:list, :new, :create, :copy, :statistics, :new_join,
- :course, :enterprise_course, :course_enterprise,:view_homework_attaches]
- before_filter :authorize, :only => [:show, :settings, :edit, :sort_project_members, :update, :modules, :close,
- :reopen,:view_homework_attaches,:course]
before_filter :find_project, :except => [ :index, :search,:list, :new, :create, :copy, :statistics, :new_join, :course, :enterprise_course, :course_enterprise,:view_homework_attaches,:join_project]
- # before_filter :authorize, :except => [:new_join, :new_homework, :homework, :statistics, :search, :watcherlist, :index, :list, :new, :create, :copy, :archive, :unarchive, :destroy, :member, :focus, :file,
- # :statistics, :feedback, :course, :enterprise_course, :course_enterprise, :project_respond, :share,
- # :show_projects_score, :issue_score_index, :news_score_index, :file_score_index, :code_submit_score_index, :projects_topic_score_index]
- #此条勿删 课程相关权限 ,:new_homework,:homework,:feedback,,:member
before_filter :authorize, :only => [:show, :settings, :edit, :sort_project_members, :update, :modules, :close, :reopen,:view_homework_attaches,:course]
before_filter :authorize_global, :only => [:new, :create,:view_homework_attaches]
before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy, :calendar]
@@ -181,6 +173,7 @@ class ProjectsController < ApplicationController
@project.safe_attributes = params[:project]
@project.organization_id = params[:organization_id]
@project.user_id = User.current.id
+ @project.project_new_type = 1
if validate_parent_id && @project.save
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
# Add current user as a project member if he is not admin
@@ -331,6 +324,28 @@ class ProjectsController < ApplicationController
@member ||= @project.members.new
@trackers = Tracker.sorted.all
@wiki ||= @project.wiki
+ @select_tab = params[:tab]
+
+ # 处理从新建版本库返回来的错误信息
+ if !params[:repository_error_message].to_s.blank?
+ html = ""
+ errors = params[:repository_error_message].flatten
+ errors.each do |error|
+ # 版本库路径为空的错误信息不予提示
+ if(error!=l(:label_repository_path_not_null))
+ html << error << ";"
+ end
+ end
+ if params[:repository] == "pswd_is_null"
+ html << l(:label_password_not_null)
+ end
+ flash[:error] = html if !html.to_s.blank?
+ end
+ scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
+ @repository = Repository.factory(scm)
+ @repository.is_default = @project.repository.nil?
+ @repository.project = @project
+
end
def send_mail_to_member
@@ -379,7 +394,7 @@ class ProjectsController < ApplicationController
# include CoursesHelper
def member
## 有角色参数的才是课程,没有的就是项目
- @render_file = 'member_list'
+ @render_file = 'project_member_list'
# 判断是否课程
if @project.project_type == Project::ProjectType_course
@teachers= searchTeacherAndAssistant(@project)
@@ -455,22 +470,10 @@ class ProjectsController < ApplicationController
def update
@project.safe_attributes = params[:project]
@project.organization_id = params[:organization_id]
- #@project.dts_test = params[:project][:dts_test]
+ params[:project][:is_public] ? @project.is_public = 1 : @project.is_public = 0
+ params[:project][:hidden_repo] ? @project.hidden_repo = 1 : @project.hidden_repo = 0
if validate_parent_id && @project.save
- @course = Course.find_by_extra(@project.identifier)
- unless @course.nil?
- @course.password = params[:project][:course][:password]
- # added by bai
- @course.term = params[:term]
- @course.time = params[:time]
- @course.setup_time = params[:setup_time]
- @course.endup_time = params[:endup_time]
- @course.class_period = params[:class_period]
- # end
- @course.save
- end
@project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
-
if params[:project][:is_public] == '0'
project_status = ProjectStatus.find_by_project_id(@project.id)
project_status.destroy if project_status
@@ -618,6 +621,17 @@ class ProjectsController < ApplicationController
end
end
+ #朋友圈、科研组、开发组之间的切换
+ def change_project_type
+ @project.project_new_type = params[:project_type]
+ if @project.save
+ message = @project.project_new_type
+ else
+ message = "0"
+ end
+ render :json => message
+ end
+
private
def memberAccess
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 01e470c91..c90cc1cc3 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -25,6 +25,7 @@ class ChangesetNotFound < Exception; end
class InvalidRevisionParam < Exception; end
class RepositoriesController < ApplicationController
+ include ApplicationHelper
menu_item :repository
menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
default_search_scope :changesets
@@ -122,7 +123,7 @@ update
if request.post? && @repository.save
redirect_to settings_project_url(@project, :tab => 'repositories')
else
- render :action => 'new'
+ redirect_to settings_project_url(@project, :tab => 'repositories')
end
else # 原逻辑
##xianbo
@@ -167,11 +168,12 @@ update
@repository.update_attributes(:login => User.current.login.to_s)
end
- redirect_to settings_project_url(@project, :tab => 'repositories')
- else if(@repository_tag)
- render :action => 'newrepo', :layout =>'base_projects'
+ redirect_to settings_project_url(@project, :tab => 'repositories',:repository_error_message=>@repository.errors.full_messages)
+ else if(@repository_tag.blank?)
+ #render :action => 'newrepo', :layout =>'base_projects'
+ redirect_to settings_project_url(@project, :tab => 'repositories',:repository => "pswd_is_null",:repository_error_message=>@repository.errors.full_messages)
else
- render :action => 'new', :layout =>'base_projects'
+ redirect_to settings_project_url(@project, :tab => 'repositories',:repository => @repository,:repository_error_message=>@repository.errors.full_messages)
end
end
diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb
index 57f26103c..defc36868 100644
--- a/app/controllers/versions_controller.rb
+++ b/app/controllers/versions_controller.rb
@@ -85,13 +85,14 @@ class VersionsController < ApplicationController
end
def new
- @version = @project.versions.build
- @version.safe_attributes = params[:version]
-
- respond_to do |format|
- format.html
- format.js
- end
+ # @version = @project.versions.build
+ # @version.safe_attributes = params[:version]
+ #
+ # respond_to do |format|
+ # format.html
+ # format.js
+ # end
+ redirect_to settings_project_url(@project, :tab => 'versions')
end
def create
@@ -116,7 +117,8 @@ class VersionsController < ApplicationController
end
else
respond_to do |format|
- format.html { render :action => 'new' }
+ format.html { flash[:error] = @version.errors.full_messages.flatten.to_s
+ redirect_to settings_project_url(@project, :tab => 'versions') }
format.js { render :action => 'new' }
format.api { render_validation_errors(@version) }
end
@@ -136,7 +138,7 @@ class VersionsController < ApplicationController
respond_to do |format|
format.html {
flash[:notice] = l(:notice_successful_update)
- redirect_back_or_default settings_project_path(@project, :tab => 'versions')
+ redirect_to settings_project_path(@project, :tab => 'versions')
}
format.api { render_api_ok }
end
diff --git a/app/helpers/account_helper.rb b/app/helpers/account_helper.rb
index 445a1670e..7ad6fe65b 100644
--- a/app/helpers/account_helper.rb
+++ b/app/helpers/account_helper.rb
@@ -1,62 +1,62 @@
-# encoding: utf-8
-#
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-module AccountHelper
-
- def email_activation_register(user, &block)
- token = Token.new(:user => user, :action => "register")
- if user.save and token.save
- UserStatus.create(:user_id => user.id, :changsets_count => 0, :watchers_count => 0)
- Mailer.run.register(token)
- #flash[:notice] = l(:notice_account_register_done)
- #render action: 'email_valid', locals: {:mail => user.mail}
- else
- yield if block_given?
- end
- user
- end
-
- def automatically_register(user, &block)
- # Automatic activation
- user.activate
- user.last_login_on = Time.now
- if user.save
- UserStatus.create(:user_id => user.id, :changsets_count => 0, :watchers_count => 0)
- #self.logged_user = user
- #flash[:notice] = l(:notice_account_activated)
- #redirect_to my_account_url
- else
- yield if block_given?
- end
- user
- end
-
- def administrator_manually__register(user, &block)
- if user.save
- UserStatus.create(:user_id => user.id ,:changsets_count => 0, :watchers_count => 0)
- # Sends an email to the administrators
- Mailer.run.account_activation_request(user)
- #account_pending
- else
- yield if block_given?
- end
- user
- end
-
-end
+# encoding: utf-8
+#
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+module AccountHelper
+
+ def email_activation_register(user, &block)
+ token = Token.new(:user => user, :action => "register")
+ if user.save and token.save
+ UserStatus.create(:user_id => user.id, :changsets_count => 0, :watchers_count => 0)
+ Mailer.run.register(token)
+ #flash[:notice] = l(:notice_account_register_done)
+ #render action: 'email_valid', locals: {:mail => user.mail}
+ else
+ yield if block_given?
+ end
+ user
+ end
+
+ def automatically_register(user, &block)
+ # Automatic activation
+ user.activate
+ user.last_login_on = Time.now
+ if user.save
+ UserStatus.create(:user_id => user.id, :changsets_count => 0, :watchers_count => 0)
+ #self.logged_user = user
+ #flash[:notice] = l(:notice_account_activated)
+ #redirect_to my_account_url
+ else
+ yield if block_given?
+ end
+ user
+ end
+
+ def administrator_manually__register(user, &block)
+ if user.save
+ UserStatus.create(:user_id => user.id ,:changsets_count => 0, :watchers_count => 0)
+ # Sends an email to the administrators
+ Mailer.run.account_activation_request(user)
+ #account_pending
+ else
+ yield if block_given?
+ end
+ user
+ end
+
+end
diff --git a/app/helpers/activities_helper.rb b/app/helpers/activities_helper.rb
index 2f48ba87f..ede2ed78a 100644
--- a/app/helpers/activities_helper.rb
+++ b/app/helpers/activities_helper.rb
@@ -1,45 +1,45 @@
-# encoding: utf-8
-#
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-module ActivitiesHelper
- def sort_activity_events(events)
- events_by_group = events.group_by(&:event_group)
- sorted_events = []
- events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event|
- if group_events = events_by_group.delete(event.event_group)
- group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i|
- sorted_events << [e, i > 0] unless e.event_description.nil?
- end
- end
- end
- sorted_events
- end
- def sort_activity_events_course(events)
- events_by_group = events.group_by(&:event_group)
- sorted_events = []
- events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event|
- if group_events = events_by_group.delete(event.event_group)
- group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i|
- sorted_events << e unless e.event_description.nil?
- end
- end
- end
- sorted_events
- end
-end
+# encoding: utf-8
+#
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+module ActivitiesHelper
+ def sort_activity_events(events)
+ events_by_group = events.group_by(&:event_group)
+ sorted_events = []
+ events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event|
+ if group_events = events_by_group.delete(event.event_group)
+ group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i|
+ sorted_events << [e, i > 0] unless e.event_description.nil?
+ end
+ end
+ end
+ sorted_events
+ end
+ def sort_activity_events_course(events)
+ events_by_group = events.group_by(&:event_group)
+ sorted_events = []
+ events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event|
+ if group_events = events_by_group.delete(event.event_group)
+ group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i|
+ sorted_events << e unless e.event_description.nil?
+ end
+ end
+ end
+ sorted_events
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 15c34c6ee..20175dc57 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -81,7 +81,7 @@ module ApplicationHelper
def authorize_for(controller, action)
User.current.allowed_to?({:controller => controller, :action => action}, @project)
end
-
+
# add by nwb
def authorize_for_course(controller, action)
User.current.allowed_to?({:controller => controller, :action => action}, @course)
@@ -128,6 +128,24 @@ module ApplicationHelper
end
end
+ def link_to_isuue_user(user, options={})
+ if user.is_a?(User)
+ name = h(user.name(options[:format]))
+ link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.user_domain}, :class => "pro_info_p"
+ else
+ h(user.to_s)
+ end
+ end
+
+ def link_to_settings_user(user, options={})
+ if user.is_a?(User)
+ name = h(user.name(options[:format]))
+ link_to name, {:controller=> 'users', :action => 'show', id: user.id, host: Setting.user_domain}, :class => "w90 c_orange fl"
+ else
+ h(user.to_s)
+ end
+ end
+
#重载上面方法,增加样式显示
def link_to_user_header user,canShowRealName=false,options={}
if user.is_a?(User)
@@ -170,6 +188,28 @@ module ApplicationHelper
s
end
+ def link_to_issue_version(issue, options={})
+ title = nil
+ subject = nil
+ text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
+ if options[:subject] == false
+ title = truncate(issue.subject, :length => 60)
+ else
+ subject = issue.subject
+ if options[:truncate]
+ subject = truncate(subject, :length => 60)
+ end
+ end
+ if issue.status_id == 5
+ s = link_to text, issue_path(issue), :class => "text_line_s", :title => title
+ else
+ s = link_to text, issue_path(issue), :class => "c_blue", :title => title
+ end
+ s << h(": #{subject}") if subject
+ s = h("#{issue.project} - ") + s if options[:project]
+ s
+ end
+
# Generates a link to an attachment.
# Options:
# * :text - Link text (default to attachment filename)
@@ -193,7 +233,7 @@ module ApplicationHelper
route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path
html_options = options.slice!(:only_path)
url = send(route_method, attachment, attachment.filename, options)
- url << "?token=#{token}" unless token.nil?
+ url << "?token=#{token}" unless token.nil?
link_to text, url, html_options
end
@@ -218,18 +258,18 @@ module ApplicationHelper
h(text),
{:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev},
:title => l(:label_revision_id, format_revision(revision))
- )
+ )
end
# Generates a link to a message
def link_to_message(message, options={}, html_options = nil)
link_to(
- truncate(message.subject, :length => 60),
- board_message_path(message.board_id, message.parent_id || message.id, {
- :r => (message.parent_id && message.id),
- :anchor => (message.parent_id ? "message-#{message.id}" : nil)
- }.merge(options)),
- html_options
+ truncate(message.subject, :length => 60),
+ board_message_path(message.board_id, message.parent_id || message.id, {
+ :r => (message.parent_id && message.id),
+ :anchor => (message.parent_id ? "message-#{message.id}" : nil)
+ }.merge(options)),
+ html_options
)
end
@@ -281,8 +321,22 @@ module ApplicationHelper
def thumbnail_tag(attachment)
link_to image_tag(thumbnail_path(attachment)),
- named_attachment_path(attachment, attachment.filename),
- :title => attachment.filename
+ named_attachment_path(attachment, attachment.filename),
+ :title => attachment.filename
+ end
+
+ def thumbnail_issue_tag(attachment)
+ imagesize = attachment.thumbnail(:size => "50*50")
+ imagepath = named_attachment_path(attachment, attachment.filename)
+ if imagesize
+ link_to image_tag(imagesize),
+ imagepath,
+ :title => attachment.filename
+ else
+ link_to image_tag(imagepath , height: '73', width: '100'),
+ imagepath,
+ :title => attachment.filename
+ end
end
# 图片缩略图链接
@@ -310,9 +364,9 @@ module ApplicationHelper
def image_to_function(name, function, html_options = {})
html_options.symbolize_keys!
tag(:input, html_options.merge({
- :type => "image", :src => image_path(name),
- :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
- }))
+ :type => "image", :src => image_path(name),
+ :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
+ }))
end
def format_activity_title(text)
@@ -330,9 +384,9 @@ module ApplicationHelper
def format_version_name(version)
if version.project == @project
- h(version)
+ h(truncate(version.name,:length=>20))
else
- h("#{version.project} - #{version}")
+ h("#{version.project} - #{truncate(version.name,:length=>20)}")
end
end
@@ -346,7 +400,7 @@ module ApplicationHelper
# The given collection may be a subset of the whole project tree
# (eg. some intermediate nodes are private and can not be seen)
#Modified by nie.
- def render_project_nested_lists(projects)
+ def render_project_nested_lists(projects)
s = ''
if projects.any?
ancestors = []
@@ -375,9 +429,9 @@ module ApplicationHelper
if project.try(:project_type) == Project::ProjectType_project
unless User.current.member_of?(@project)
- s << ""
- s << watcher_link(@project, User.current)#, ['whiteButton'])
- s << " "
+ s << ""
+ s << watcher_link(@project, User.current)#, ['whiteButton'])
+ s << " "
end
s << (render :partial => 'projects/project', :locals => {:project => project}).to_s
else
@@ -390,7 +444,7 @@ module ApplicationHelper
@project = original_project
end
s.html_safe
- end
+ end
def render_course_nested_lists(courses)
s = ''
@@ -425,7 +479,7 @@ module ApplicationHelper
end
- #added by young
+ #added by young
def render_project_nested_lists_new(projects)
s = ''
if projects.any?
@@ -454,7 +508,7 @@ module ApplicationHelper
end
s.html_safe
end
- #end
+ #end
def render_page_hierarchy(pages, node=nil, options={})
content = ''
if pages[node]
@@ -489,14 +543,22 @@ module ApplicationHelper
end
end
+ def render_project_settings_tabs(tabs)
+ if tabs.any?
+ render :partial => 'common/project_tab', :locals => {:tabs => tabs}
+ else
+ content_tag 'p', l(:label_no_data), :class => "nodata"
+ end
+ end
+
# Renders the project quick-jump box
def render_project_jump_box
return unless User.current.logged?
projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
if projects.any?
options =
- ("#{ l(:label_jump_to_a_project) } " +
- '--- ').html_safe
+ ("#{ l(:label_jump_to_a_project) } " +
+ '--- ').html_safe
options << project_tree_options_for_select(projects, :selected => @project) do |p|
{ :value => project_path(:id => p, :jump => current_menu_item) }
@@ -547,6 +609,17 @@ module ApplicationHelper
s.html_safe
end
+ #缺陷追踪者列表复选框生成
+ def issue_watcher_check_box_tags_ex name, principals
+ s = ''
+ principals.each do |principal|
+ s << "
#{ check_box_tag name, principal.id, false, :id => nil } #{h link_to principal.userInfo, user_path( principal.id)} \n"
+ end
+ s.html_safe
+ end
+
+
+
#扩展的checkbox生成
def principals_check_box_tags_ex(name, principals)
s = ''
@@ -556,6 +629,15 @@ module ApplicationHelper
s.html_safe
end
+ # li标签checkbos扩展
+ def principals_check_box_tags_li(name, principals)
+ s = ''
+ principals.each do |principal|
+ s << "#{ check_box_tag name, principal.id, false, :id => nil } #{h link_to principal.userInfo, user_path( principal.id), :class => "c_blue" } \n"
+ end
+ s.html_safe
+ end
+
#扩展的checkbox生成
def principals_radio_box_tags_ex(name, principals)
s = ''
@@ -666,24 +748,24 @@ module ApplicationHelper
link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
url.merge({"#{name}[move_to]" => 'highest'}),
:method => method, :title => l(:label_sort_highest)) +
- link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
- url.merge({"#{name}[move_to]" => 'higher'}),
- :method => method, :title => l(:label_sort_higher)) +
- link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
- url.merge({"#{name}[move_to]" => 'lower'}),
- :method => method, :title => l(:label_sort_lower)) +
- link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
- url.merge({"#{name}[move_to]" => 'lowest'}),
- :method => method, :title => l(:label_sort_lowest))
+ link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
+ url.merge({"#{name}[move_to]" => 'higher'}),
+ :method => method, :title => l(:label_sort_higher)) +
+ link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
+ url.merge({"#{name}[move_to]" => 'lower'}),
+ :method => method, :title => l(:label_sort_lower)) +
+ link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
+ url.merge({"#{name}[move_to]" => 'lowest'}),
+ :method => method, :title => l(:label_sort_lowest))
end
def breadcrumb(*args)
elements = args.flatten
- elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
+ elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'wiki_con_tit"') : nil
end
def other_formats_links(&block)
- concat(''.html_safe + l(:label_export_to))
+ concat('
'.html_safe + l(:label_export_to))
yield Redmine::Views::OtherFormatsBuilder.new(self)
concat('
'.html_safe)
end
@@ -758,15 +840,15 @@ module ApplicationHelper
def textilizable(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
case args.size
- when 1
- obj = options[:object]
- text = args.shift
- when 2
- obj = args.shift
- attr = args.shift
- text = obj.send(attr).to_s
- else
- raise ArgumentError, 'invalid arguments to textilizable'
+ when 1
+ obj = options[:object]
+ text = args.shift
+ when 2
+ obj = args.shift
+ attr = args.shift
+ text = obj.send(attr).to_s
+ else
+ raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if text.blank?
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
@@ -922,18 +1004,18 @@ module ApplicationHelper
# check if page exists
wiki_page = link_project.wiki.find_page(page)
url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
- "##{anchor}"
- else
- case options[:wiki_links]
- when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
- when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
- else
- wiki_page_id = page.present? ? Wiki.titleize(page) : nil
- parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
- url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
- :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
- end
- end
+ "##{anchor}"
+ else
+ case options[:wiki_links]
+ when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
+ when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
+ else
+ wiki_page_id = page.present? ? Wiki.titleize(page) : nil
+ parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil
+ url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project,
+ :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent)
+ end
+ end
link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
else
# project or wiki doesn't exist
@@ -1008,110 +1090,110 @@ module ApplicationHelper
# project.changesets.visible raises an SQL error because of a double join on repositories
if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision},
- :class => 'changeset',
- :title => truncate_single_line(changeset.comments, :length => 100))
+ :class => 'changeset',
+ :title => truncate_single_line(changeset.comments, :length => 100))
end
end
elsif sep == '#'
oid = identifier.to_i
case prefix
- when nil
- if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
- anchor = comment_id ? "note-#{comment_id}" : nil
- link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
- :class => issue.css_classes,
- :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
- end
- when 'document'
- if document = Document.visible.find_by_id(oid)
- link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
- :class => 'document'
- end
- when 'version'
- if version = Version.visible.find_by_id(oid)
- link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
- :class => 'version'
- end
- when 'message'
- if message = Message.visible.find_by_id(oid, :include => :parent)
- link = link_to_message(message, {:only_path => only_path}, :class => 'message')
- end
- when 'forum'
- if board = Board.visible.find_by_id(oid)
- link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
- :class => 'board'
- end
- when 'news'
- if news = News.visible.find_by_id(oid)
- link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
- :class => 'news'
- end
- when 'project'
- if p = Project.visible.find_by_id(oid)
- link = link_to_project(p, {:only_path => only_path}, :class => 'project')
- end
+ when nil
+ if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
+ anchor = comment_id ? "note-#{comment_id}" : nil
+ link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
+ :class => issue.css_classes,
+ :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
+ end
+ when 'document'
+ if document = Document.visible.find_by_id(oid)
+ link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
+ :class => 'document'
+ end
+ when 'version'
+ if version = Version.visible.find_by_id(oid)
+ link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
+ :class => 'version'
+ end
+ when 'message'
+ if message = Message.visible.find_by_id(oid, :include => :parent)
+ link = link_to_message(message, {:only_path => only_path}, :class => 'message')
+ end
+ when 'forum'
+ if board = Board.visible.find_by_id(oid)
+ link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
+ :class => 'board'
+ end
+ when 'news'
+ if news = News.visible.find_by_id(oid)
+ link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
+ :class => 'news'
+ end
+ when 'project'
+ if p = Project.visible.find_by_id(oid)
+ link = link_to_project(p, {:only_path => only_path}, :class => 'project')
+ end
end
elsif sep == ':'
# removes the double quotes if any
name = identifier.gsub(%r{^"(.*)"$}, "\\1")
case prefix
- when 'document'
- if project && document = project.documents.visible.find_by_title(name)
- link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
- :class => 'document'
- end
- when 'version'
- if project && version = project.versions.visible.find_by_name(name)
- link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
- :class => 'version'
- end
- when 'forum'
- if project && board = project.boards.visible.find_by_name(name)
- link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
- :class => 'board'
- end
- when 'news'
- if project && news = project.news.visible.find_by_title(name)
- link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
- :class => 'news'
- end
- when 'commit', 'source', 'export'
- if project
- repository = nil
- if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
- repo_prefix, repo_identifier, name = $1, $2, $3
- repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
- else
- repository = project.repository
+ when 'document'
+ if project && document = project.documents.visible.find_by_title(name)
+ link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
+ :class => 'document'
+ end
+ when 'version'
+ if project && version = project.versions.visible.find_by_name(name)
+ link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
+ :class => 'version'
+ end
+ when 'forum'
+ if project && board = project.boards.visible.find_by_name(name)
+ link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
+ :class => 'board'
+ end
+ when 'news'
+ if project && news = project.news.visible.find_by_title(name)
+ link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
+ :class => 'news'
end
- if prefix == 'commit'
- if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
- link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
- :class => 'changeset',
- :title => truncate_single_line(changeset.comments, :length => 100)
+ when 'commit', 'source', 'export'
+ if project
+ repository = nil
+ if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
+ repo_prefix, repo_identifier, name = $1, $2, $3
+ repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
+ else
+ repository = project.repository
end
- else
- if repository && User.current.allowed_to?(:browse_repository, project)
- name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
- path, rev, anchor = $1, $3, $5
- link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
- :path => to_path_param(path),
- :rev => rev,
- :anchor => anchor},
- :class => (prefix == 'export' ? 'source download' : 'source')
+ if prefix == 'commit'
+ if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
+ link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
+ :class => 'changeset',
+ :title => truncate_single_line(changeset.comments, :length => 100)
+ end
+ else
+ if repository && User.current.allowed_to?(:browse_repository, project)
+ name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
+ path, rev, anchor = $1, $3, $5
+ link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
+ :path => to_path_param(path),
+ :rev => rev,
+ :anchor => anchor},
+ :class => (prefix == 'export' ? 'source download' : 'source')
+ end
end
+ repo_prefix = nil
+ end
+ when 'attachment'
+ attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
+ if attachments && attachment = Attachment.latest_attach(attachments, name)
+ link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
+ end
+ when 'project'
+ if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
+ link = link_to_project(p, {:only_path => only_path}, :class => 'project')
end
- repo_prefix = nil
- end
- when 'attachment'
- attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
- if attachments && attachment = Attachment.latest_attach(attachments, name)
- link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
- end
- when 'project'
- if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
- link = link_to_project(p, {:only_path => only_path}, :class => 'project')
- end
end
end
end
@@ -1128,9 +1210,9 @@ module ApplicationHelper
@current_section += 1
if @current_section > 1
content_tag('div',
- link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
- :class => 'contextual',
- :title => l(:button_edit_section)) + heading.html_safe
+ link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
+ :class => 'contextual',
+ :title => l(:button_edit_section)) + heading.html_safe
else
heading
end
@@ -1249,10 +1331,10 @@ module ApplicationHelper
# Same as Rails' simple_format helper without using paragraphs
def simple_format_without_paragraph(text)
text.to_s.
- gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
- gsub(/\n\n+/, " "). # 2+ newline -> 2 br
- gsub(/([^\n]\n)(?=[^\n])/, '\1 '). # 1 newline -> br
- html_safe
+ gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
+ gsub(/\n\n+/, " "). # 2+ newline -> 2 br
+ gsub(/([^\n]\n)(?=[^\n])/, '\1 '). # 1 newline -> br
+ html_safe
end
def wiki_simple_format_without_paragraph(text)
@@ -1267,7 +1349,7 @@ module ApplicationHelper
end
def lang_options_for_select(blank=true)
- { 'Chinese简体中文 '=> 'zh', :English => :en}
+ { 'Chinese简体中文 '=> 'zh', :English => :en}
end
def label_tag_for(name, option_tags = nil, options = {})
@@ -1334,9 +1416,31 @@ module ApplicationHelper
def delete_link(url, options={})
options = {
- :method => :delete,
- :data => {:confirm => l(:text_are_you_sure)},
- :class => 'icon icon-del'
+ :method => :delete,
+ :data => {:confirm => l(:text_are_you_sure)},
+ :class => 'icon icon-del'
+ }.merge(options)
+
+ link_to l(:button_delete), url, options
+ end
+
+ def delete_link_version(url, options={})
+ options = {
+ :method => :delete,
+ :data => {:confirm => l(:text_are_you_sure)},
+ :class => 'c_purple'
+ }.merge(options)
+
+ link_to l(:button_delete), url, options
+ end
+
+
+
+ def delete_new_link(url, options={})
+ options = {
+ :method => :delete,
+ :data => {:confirm => l(:text_are_you_sure)},
+ :class => "c_purple"
}.merge(options)
link_to l(:button_delete), url, options
@@ -1347,11 +1451,11 @@ module ApplicationHelper
:href => "#",
:onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|,
:accesskey => accesskey(:preview)
- }.merge(options)
+ }.merge(options)
end
def link_to_function(name, function, html_options={})
- content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options))
+ content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(:class => " c_purple"))
end
# Helper to render JSON in views
@@ -1373,9 +1477,8 @@ module ApplicationHelper
end
def check_all_links(form_name)
- link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
- " | ".html_safe +
- link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
+ link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + " ".html_safe + " | "+ " ".html_safe +
+ link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
end
def progress_bar(pcts, options={})
@@ -1386,12 +1489,12 @@ module ApplicationHelper
width = options[:width] || '100px;'
legend = options[:legend] || ''
content_tag('table',
- content_tag('tr',
- (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
- (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
- (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
- ), :class => 'progress', :style => "width: #{width};").html_safe +
- content_tag('p', legend, :class => 'percent').html_safe
+ content_tag('tr',
+ (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
+ (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
+ (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
+ ), :class => 'progress', :style => "width: #{width};").html_safe +
+ content_tag('p', legend, :class => 'percent').html_safe
end
def checked_image(checked=true)
@@ -1404,7 +1507,7 @@ module ApplicationHelper
unless @context_menu_included
content_for :header_tags do
javascript_include_tag('context_menu') +
- stylesheet_link_tag('context_menu')
+ stylesheet_link_tag('context_menu')
end
if l(:direction) == 'rtl'
content_for :header_tags do
@@ -1435,7 +1538,7 @@ module ApplicationHelper
tags = javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
- path_to_image('/images/calendar.png') +
+ path_to_image('/images/public_icon.png') +
"', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true};")
jquery_locale = l('jquery.locale', :default => current_language.to_s)
unless jquery_locale == 'en'
@@ -1457,7 +1560,7 @@ module ApplicationHelper
tags = javascript_tag(
"var datepickerOptions={dateFormat: 'yy-mm-dd',minDate: new Date(), firstDay: #{start_of_week}, " +
"showOn: 'button', buttonImageOnly: true, buttonImage: '" +
- path_to_image('/images/calendar.png') +
+ path_to_image('/images/public_icon.png') +
"', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true, onClose: function(dateText, inst) {TimeClose(dateText,inst);}, beforeShow : function(input){TimeBeforeShow(input);} };")
jquery_locale = l('jquery.locale', :default => current_language.to_s)
unless jquery_locale == 'en'
@@ -1663,7 +1766,7 @@ module ApplicationHelper
end
s
end
-
+
def get_memo
@new_memo = Memo.new
#@new_memo.subject = "有什么想说的,尽管来咆哮吧~~"
@@ -1717,6 +1820,17 @@ module ApplicationHelper
candown
end
+ def project_type_link(text, value)
+ if value == 1
+ link_to " #{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type"
+ elsif value == 2
+ link_to " #{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type"
+ else
+ link_to " #{text}".html_safe,"javascript:void(0)" ,:onClick => "show_window();", :class => "pr_join_a",:id => "setting_project_type"
+ end
+
+ end
+
private
def wiki_helper
@@ -1746,11 +1860,11 @@ module ApplicationHelper
html << (content_tag "span", l(:label_no_current_watchers))
end
for user in User.watched_by(obj.id)
- html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}")
- count = count + 1
- if count >= 12
- break
- end
+ html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}")
+ count = count + 1
+ if count >= 12
+ break
+ end
end
html.html_safe
end
@@ -1776,13 +1890,13 @@ module ApplicationHelper
html.html_safe
end
- def show_bid_fans_picture(obj)
+ def show_bid_fans_picture(obj)
html = ''
if obj.watcher_users.count == 0
html << (content_tag "span", l(:label_project_no_follow))
else
obj.watcher_users.take(12).each do |user|
- html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
+ html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
end
end
html.html_safe
@@ -1817,7 +1931,7 @@ module ApplicationHelper
html.html_safe
end
- def show_contest_project(contest)
+ def show_contest_project(contest)
html = ''
if contest.projects.where('is_public = 1').count == 0
html << (content_tag "p", l(:label_no_bid_project), :class => "font_lighter")
@@ -1829,7 +1943,7 @@ module ApplicationHelper
html.html_safe
end
- def show_contest_softapplication(contest)
+ def show_contest_softapplication(contest)
html = ''
if contest.softapplications.where('is_public = 1').count == 0
html << (content_tag "p", l(:label_no_contest_softapplication), :class => "font_lighter")
@@ -1841,17 +1955,17 @@ module ApplicationHelper
html.html_safe
end
- def show_contest_fans_picture(obj)
+ def show_contest_fans_picture(obj)
html = ''
if obj.watcher_users.count == 0
html << (content_tag "span", l(:label_project_no_follow))
else
obj.watcher_users.take(12).each do |user|
- html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
+ html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
end
end
html.html_safe
- end
+ end
#display fans picture
def show_more_fans?(obj)
@@ -1868,13 +1982,13 @@ module ApplicationHelper
html << (content_tag "span", l(:label_no_current_fans))
else
obj.watcher_users.take(12).each do |user|
- html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
+ html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => user.name)
end
end
html.html_safe
end
- # added by bai
+ # added by bai
def show_more_participate?(obj)
if obj.join_in_contests.count > 12
return true
@@ -1883,18 +1997,18 @@ module ApplicationHelper
end
end
- def show_participate_picture(obj)
+ def show_participate_picture(obj)
html = ''
count = 0
if obj.join_in_contests.count == 0
html << (content_tag "span", l(:label_no_current_participate))
end
for temp in obj.join_in_contests
- html << (link_to image_tag(url_to_avatar(temp.user), :class => "avatar"), user_path(temp.user), :class => "avatar", :title => "#{temp.user.name}")
- count = count + 1
- if count >= 12
- break
- end
+ html << (link_to image_tag(url_to_avatar(temp.user), :class => "avatar"), user_path(temp.user), :class => "avatar", :title => "#{temp.user.name}")
+ count = count + 1
+ if count >= 12
+ break
+ end
end
html.html_safe
end
@@ -1903,14 +2017,14 @@ module ApplicationHelper
# add by huang
def show_watcher_list(user)
- html = ''
- count = 0
+ html = ''
+ count = 0
for user in User.watched_by(user.id)
- html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}")
- count = count + 1
- if count >= 12
- break
- end
+ html << (link_to image_tag(url_to_avatar(user), :class => "avatar"), user_path(user), :class => "avatar", :title => "#{user.name}")
+ count = count + 1
+ if count >= 12
+ break
+ end
end
html.html_safe
end
@@ -1928,14 +2042,14 @@ module ApplicationHelper
return true if bid.nil?
case bid.homework_type
- when Bid::HomeworkFile
- attaches = HomeworkAttach.where(bid_id: curb)
- attaches.map(&:user_id).include? cur
- when Bid::HomeworkProject
- attaches = BidingProject.where(user_id: User.current, bid_id: bid)
- attaches.count > 0 # > 0 则有提交记录
- else
- true
+ when Bid::HomeworkFile
+ attaches = HomeworkAttach.where(bid_id: curb)
+ attaches.map(&:user_id).include? cur
+ when Bid::HomeworkProject
+ attaches = BidingProject.where(user_id: User.current, bid_id: bid)
+ attaches.count > 0 # > 0 则有提交记录
+ else
+ true
end
end
@@ -1964,6 +2078,8 @@ module ApplicationHelper
forum_link = link_to l(:label_forum_all), {:controller => "forums", :action => "index"}
stores_link = link_to l(:label_stores_index), {:controller => 'stores', :action=> 'index'}
school_all_school_link = link_to l(:label_school_all), {:controller => 'school', :action => 'index'}
+ project_new_link = link_to l(:label_project_new), {:controller => 'projects', :action => 'new', :host => Setting.project_domain}
+ # project_mine_link = link_to l(:label_my_project), {:controller => 'users', :action => 'user_projects', :host => Setting.project_domain}
#@nav_dispaly_project_label
nav_list = Array.new
@@ -1976,6 +2092,8 @@ module ApplicationHelper
nav_list.push(main_contest_link) if @nav_dispaly_main_contest_label && @show_contest == 1 && visiable
nav_list.push(courses_link) if @nav_dispaly_course_label && @show_course == 1 && visiable
+ nav_list.push(project_new_link) if @nav_dispaly_project_label
+ # nav_list.push(project_mine_link) if @nav_dispaly_main_project_label
# nav_list.push(projects_link) if @nav_dispaly_project_label
#nav_list.push(users_link) if @nav_dispaly_user_label
# nav_list.push(contest_link) if @nav_dispaly_contest_label && @show_contest == 1
@@ -1995,12 +2113,12 @@ module ApplicationHelper
end
# def hadcommittedforcontest(curu)
- # message = JournalsForMessage.find_by_sql("select * from journals_for_messages where jour_type = 'Softapplication' ")
- # message.each do |createmessage|
- # if createmessage.user_id == curu
- # return true
- # end
- # end
+ # message = JournalsForMessage.find_by_sql("select * from journals_for_messages where jour_type = 'Softapplication' ")
+ # message.each do |createmessage|
+ # if createmessage.user_id == curu
+ # return true
+ # end
+ # end
# end
def footer_logo(ul_class=nil, li_class=nil)
@@ -2020,16 +2138,16 @@ module ApplicationHelper
def sort_homework_path(bid, sort, direction)
case self.action_name
- when 'show_courseEx'
- get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: 'asc')
- when 'get_not_batch_homework'
- get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
- when 'get_batch_homeworks'
- get_batch_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
- when 'get_homeworks'
- get_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
- else
- '#'
+ when 'show_courseEx'
+ get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: 'asc')
+ when 'get_not_batch_homework'
+ get_not_batch_homework_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
+ when 'get_batch_homeworks'
+ get_batch_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
+ when 'get_homeworks'
+ get_homeworks_homework_attach_index_path(bid_id: bid.id, sort: sort, direction: direction)
+ else
+ '#'
end
end
diff --git a/app/helpers/attachments_helper.rb b/app/helpers/attachments_helper.rb
index 8843b1a76..be24629af 100644
--- a/app/helpers/attachments_helper.rb
+++ b/app/helpers/attachments_helper.rb
@@ -35,6 +35,16 @@ module AttachmentsHelper
end
end
+ def link_to_attachment_project(container, options = {})
+ options.assert_valid_keys(:author, :thumbnails)
+
+ if container.attachments.any?
+ options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
+ render :partial => 'attachments/project_file_links',
+ :locals => {:attachments => container.attachments, :options => options, :thumbnails => (options[:thumbnails] && Setting.thumbnails_enabled?)}
+ end
+ end
+
def link_to_attachments_course(container, options = {})
options.assert_valid_keys(:author, :thumbnails)
diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb
index 2e06e3a43..801d98b0b 100644
--- a/app/helpers/courses_helper.rb
+++ b/app/helpers/courses_helper.rb
@@ -691,9 +691,11 @@ module CoursesHelper
#end
#poll_count
- #Poll.where(polls_group_id: @course_ids, polls_type: Course, polls_status: 2||3).where("published_at>?",date_from).each do |poll|
- # activities[poll.polls_group_id]+=1
- #end
+ # 动态目前只统计发布的问卷,关闭的问卷不在动态内显示
+ # Poll.where(polls_group_id: @course_ids, polls_type: Course, polls_status: 2||3).where("published_at>?",date_from).each do |poll|
+ Poll.where(polls_group_id: @course_ids, polls_type: Course, polls_status: 2||3).where("published_at>?",date_from).each do |poll|
+ activities[poll.polls_group_id]+=1
+ end
#end
diff --git a/app/helpers/files_helper.rb b/app/helpers/files_helper.rb
index d69f13a29..9a1765ddc 100644
--- a/app/helpers/files_helper.rb
+++ b/app/helpers/files_helper.rb
@@ -1,150 +1,150 @@
-# encoding: utf-8
-module FilesHelper
- include AttachmentsHelper
-
- def downloadAll containers
- paths = []
- files = []
- tmpfile = "tmp.zip"
-
- containers.each do |container|
- next if container.attachments.empty?
- if container.is_a?(Version);end
- container.attachments.each do |attachment|
- paths << attachment.diskfile
- file = attachment.diskfile
- # logger.error "[FilesHelper] downloadAll: #{e}"
- begin
- File.new(file, "r")
- rescue Exception => e
- logger.error e
- next
- end
- files << file
- # zip.add(file.path.dup.sub(directory, ''), file.path)
- end
- end
-
- zipfile_name = "archive.zip"
- if File.exists? File.open(zipfile_name, "w+")
- ff = File.open(zipfile_name, "w+")
- ff.close
- File.delete ff
- end
- Zip::ZipFile.open(zipfile_name, Zip::ZipFile::CREATE) do |zipfile|
- files.each do |filename|
- directory = File.dirname filename
- # Two arguments:
- # - The name of the file as it will appear in the archive
- # - The original file, including the path to find it
- dir = filename.sub(directory+"/", '')
- zipfile.add(dir, filename)
-
- end
- end
- File.new(zipfile_name,'w+')
- end
-
- #带勾选框的课程列表
- def courses_check_box_tags(name,courses,current_course,attachment)
- s = ''
- courses.each do |course|
- if !course_contains_attachment?(course,attachment) && is_course_teacher(User.current,course) && course_in_current_or_next_term(course)
- s << "#{ check_box_tag name, course.id, false, :id => nil } #{h course.name} [#{get_course_term course}] "
- end
- end
- s.html_safe
- end
-
- #带勾选框的项目列表
- def projects_check_box_tags(name,projects,current_project,attachment)
- s = ''
- projects.each do |project|
- if !project_contains_attachment?(project,attachment) && User.current.allowed_to?(:manage_files, project)
- s << "#{ check_box_tag name, project.id, false, :id => nil } #{h project.name} "
- end
- end
- s.html_safe
- end
-
- #判断用户是否拥有不包含当前资源的课程,需用户在该课程中角色为教师且该课程属于当前学期或下一学期
- def has_course? user,file
- result = false
- user.courses.each do |course|
- if !course_contains_attachment?(course,file) && is_course_teacher(User.current,course) && course_in_current_or_next_term(course)
- return true
- end
- end
- result
- end
-
- #判断用户是否拥有不包含当前资源的项目,需用户在该项目中有资源管理相关资源
- def has_project? user,file
- result = false
- user.projects.each do |project|
- if !project_contains_attachment?(project,file) && User.current.allowed_to?(:manage_files, project)
- return true
- end
- end
- result
- end
-
- # 判断指定的资源时候符合类型
- def isTypeOk(attachment, type, contentType)
- result = false
- if type != 0
- if attachment.attachtype == type
- result = true
- end
- else
- result = true
- end
- if result
- if contentType != '0' && contentType != attachment.suffix_type
- result = false
- end
- end
- result
- end
-
- def visable_attachemnts attachments
- result = []
- attachments.each do |attachment|
- if attachment.is_public? ||
- (attachment.container_type == "Project" && User.current.member_of?(attachment.project)) ||
- (attachment.container_type == "Course" && User.current.member_of_course?(Course.find(attachment.container_id)))||
- attachment.author_id == User.current.id
- result << attachment
- end
- end
- result
- end
-
- def get_attachments_by_tag attachments,tag
- attachments.each do |attachment|
- attachment.tag_list.include?(tag)
- end
- end
-
- def visable_attachemnts_insite attachments,obj
- result = []
- if obj.is_a?(Course)
- attachments.each do |attachment|
- if attachment.is_public? || (attachment.container_type == "Course" && attachment.container_id == obj.id && User.current.member_of_course?(Course.find(attachment.container_id)))|| attachment.author_id == User.current.id
- result << attachment
- end
- end
- else if obj.is_a?(Project)
- attachments.each do |attachment|
- if attachment.is_public? || (attachment.container_type == "Project" && attachment.container_id == obj.id && User.current.member_of_course?(Project.find(attachment.container_id)))|| attachment.author_id == User.current.id
- result << attachment
- end
- end
- end
- end
- result
- end
-
-
-
+# encoding: utf-8
+module FilesHelper
+ include AttachmentsHelper
+
+ def downloadAll containers
+ paths = []
+ files = []
+ tmpfile = "tmp.zip"
+
+ containers.each do |container|
+ next if container.attachments.empty?
+ if container.is_a?(Version);end
+ container.attachments.each do |attachment|
+ paths << attachment.diskfile
+ file = attachment.diskfile
+ # logger.error "[FilesHelper] downloadAll: #{e}"
+ begin
+ File.new(file, "r")
+ rescue Exception => e
+ logger.error e
+ next
+ end
+ files << file
+ # zip.add(file.path.dup.sub(directory, ''), file.path)
+ end
+ end
+
+ zipfile_name = "archive.zip"
+ if File.exists? File.open(zipfile_name, "w+")
+ ff = File.open(zipfile_name, "w+")
+ ff.close
+ File.delete ff
+ end
+ Zip::ZipFile.open(zipfile_name, Zip::ZipFile::CREATE) do |zipfile|
+ files.each do |filename|
+ directory = File.dirname filename
+ # Two arguments:
+ # - The name of the file as it will appear in the archive
+ # - The original file, including the path to find it
+ dir = filename.sub(directory+"/", '')
+ zipfile.add(dir, filename)
+
+ end
+ end
+ File.new(zipfile_name,'w+')
+ end
+
+ #带勾选框的课程列表
+ def courses_check_box_tags(name,courses,current_course,attachment)
+ s = ''
+ courses.each do |course|
+ if !course_contains_attachment?(course,attachment) && is_course_teacher(User.current,course) && course_in_current_or_next_term(course)
+ s << "#{ check_box_tag name, course.id, false, :id => nil } #{h course.name} [#{get_course_term course}] "
+ end
+ end
+ s.html_safe
+ end
+
+ #带勾选框的项目列表
+ def projects_check_box_tags(name,projects,current_project,attachment)
+ s = ''
+ projects.each do |project|
+ if !project_contains_attachment?(project,attachment) && User.current.allowed_to?(:manage_files, project)
+ s << "#{ check_box_tag name, project.id, false, :id => nil } #{h project.name} "
+ end
+ end
+ s.html_safe
+ end
+
+ #判断用户是否拥有不包含当前资源的课程,需用户在该课程中角色为教师且该课程属于当前学期或下一学期
+ def has_course? user,file
+ result = false
+ user.courses.each do |course|
+ if !course_contains_attachment?(course,file) && is_course_teacher(User.current,course) && course_in_current_or_next_term(course)
+ return true
+ end
+ end
+ result
+ end
+
+ #判断用户是否拥有不包含当前资源的项目,需用户在该项目中有资源管理相关资源
+ def has_project? user,file
+ result = false
+ user.projects.each do |project|
+ if !project_contains_attachment?(project,file) && User.current.allowed_to?(:manage_files, project)
+ return true
+ end
+ end
+ result
+ end
+
+ # 判断指定的资源时候符合类型
+ def isTypeOk(attachment, type, contentType)
+ result = false
+ if type != 0
+ if attachment.attachtype == type
+ result = true
+ end
+ else
+ result = true
+ end
+ if result
+ if contentType != '0' && contentType != attachment.suffix_type
+ result = false
+ end
+ end
+ result
+ end
+
+ def visable_attachemnts attachments
+ result = []
+ attachments.each do |attachment|
+ if attachment.is_public? ||
+ (attachment.container_type == "Project" && User.current.member_of?(attachment.project)) ||
+ (attachment.container_type == "Course" && User.current.member_of_course?(Course.find(attachment.container_id)))||
+ attachment.author_id == User.current.id
+ result << attachment
+ end
+ end
+ result
+ end
+
+ def get_attachments_by_tag attachments,tag
+ attachments.each do |attachment|
+ attachment.tag_list.include?(tag)
+ end
+ end
+
+ def visable_attachemnts_insite attachments,obj
+ result = []
+ if obj.is_a?(Course)
+ attachments.each do |attachment|
+ if attachment.is_public? || (attachment.container_type == "Course" && attachment.container_id == obj.id && User.current.member_of_course?(Course.find(attachment.container_id)))|| attachment.author_id == User.current.id
+ result << attachment
+ end
+ end
+ else if obj.is_a?(Project)
+ attachments.each do |attachment|
+ if attachment.is_public? || (attachment.container_type == "Project" && attachment.container_id == obj.id && User.current.member_of_course?(Project.find(attachment.container_id)))|| attachment.author_id == User.current.id
+ result << attachment
+ end
+ end
+ end
+ end
+ result
+ end
+
+
+
end
\ No newline at end of file
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 6a708051a..b72a191db 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -61,12 +61,38 @@ module IssuesHelper
#h("#{issue.tracker} ##{issue.id}")
# h("#{issue.tracker} #{issue.source_from}")
s = ''
- s << link_to(@issue.project.name, project_issues_path(@issue.project))
- s << " > #"
- s << @issue.project_index
+ s << link_to(@issue.project.name, project_issues_path(@issue.project), :class => "pro_page_top")
+ s << " > "
+ s << link_to("#" + @issue.project_index, project_issues_path(@issue.project), :class => "pro_page_top")
s.html_safe
end
+ #获取跟踪类型
+ #REDO:时间紧需要优化,两个方法可以综合成一个
+ def get_issue_type(value)
+ if value == "缺陷" || value == 1
+ class_type = "red_btn_cir ml10"
+ elsif value == "功能" || value == 2
+ class_type = "blue_btn_cir ml10"
+ elsif value == "支持" || value == 3
+ class_type = "green_btn_cir ml10"
+ else
+ class_type = "orange_btn_cir ml10"
+ end
+ end
+
+ def get_issue_typevalue(value)
+ if value == "缺陷" || value == 1
+ assign = "缺陷"
+ elsif value == "功能" || value == 2
+ assign = "功能"
+ elsif value == "支持" || value == 3
+ assign = "支持"
+ else
+ assign = "任务"
+ end
+ end
+
def render_issue_subject_with_tree(issue)
s = ''
ancestors = issue.root? ? [] : issue.ancestors.visible.all
@@ -314,17 +340,18 @@ module IssuesHelper
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
if options[:token].nil?
- value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
+ value = atta.filename
else
- value = link_to_attachment(atta, :download => true, :only_path => options[:only_path], :token => options[:token])
- end
- if options[:only_path] != false && atta.is_text?
- value += link_to(
- image_tag('magnifier.png'),
- :controller => 'attachments', :action => 'show',
- :id => atta, :filename => atta.filename
- )
+ value = atta.filename
end
+ # 放大镜搜索功能
+ # if options[:only_path] != false && atta.is_text?
+ # value += link_to(
+ # image_tag('magnifier.png'),
+ # :controller => 'attachments', :action => 'show',
+ # :id => atta, :filename => atta.filename
+ # )
+ # end
else
value = content_tag("i", h(value)) if value
end
diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb
index cfebb4d30..0eee08b6b 100644
--- a/app/helpers/journals_helper.rb
+++ b/app/helpers/journals_helper.rb
@@ -46,6 +46,26 @@ module JournalsHelper
content.html_safe
end
+ def render_links_easy(issue, journal, options={})
+ content = ''
+ editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
+ destroyable = User.current.logged? && ((journal.user == User.current) || (issue.author_id == User.current.id) || (User.current.admin == 1))
+ links = []
+ if !journal.notes.blank?
+ links << link_to(l(:button_quote),
+ {:controller => 'journals', :action => 'new', :id => issue.id, :journal_id => journal},
+ :remote => true,
+ :method => 'post',
+ :title => l(:button_quote)) if options[:reply_links]
+ if destroyable
+ links << link_to(l(:button_delete), { :controller => 'journals', :action => 'destroy', :id => journal, :format => 'js' },
+ :title => l(:button_delete))
+ end
+ end
+ content << content_tag('div', links.join(' ').html_safe, :class => 'contextual') unless links.empty?
+ content.html_safe
+ end
+
def render_notes (issue, journal, options={})
content = ''
editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
@@ -73,6 +93,35 @@ module JournalsHelper
content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes ,:style => "width:580px")
end
+ # 缺陷回复内容、引用内容
+ # Redo:后面需要统一扩展
+ def render_notes_issue (issue, journal, options={})
+ content = ''
+ editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
+ destroyable = User.current.logged? && ((journal.user == User.current) || (issue.author_id == User.current.id) || (User.current.admin == 1))
+ links = []
+ if !journal.notes.blank?
+ links << link_to(l(:button_quote),
+ {:controller => 'journals', :action => 'new', :id => issue.id, :journal_id => journal},
+ :remote => true,
+ :method => 'post',
+ :title => l(:button_quote)) if options[:reply_links]
+ links << link_to_in_place_notes_editor(l(:button_edit), "journal-#{journal.id}-notes",
+ { :controller => 'journals', :action => 'edit', :id => journal, :format => 'js' },
+ :title => l(:button_edit)) if editable
+ #Added by young
+ if destroyable
+ links << link_to(l(:button_delete), { :controller => 'journals', :action => 'destroy', :id => journal, :format => 'js' },
+ :title => l(:button_delete))
+ end
+ end
+ #content << content_tag('div', links.join(' ').html_safe, :class => 'contextual', :style => 'margin-top:-25px;') unless links.empty?
+ content << textilizable(journal, :notes)
+ css_classes = "wiki"
+ css_classes << " editable" if editable
+ content_tag('div', content.html_safe, :id => "journal-#{journal.id}-notes", :class => css_classes ,:style => "width:510px")
+ end
+
def link_to_in_place_notes_editor(text, field_id, url, options={})
onclick = "$.ajax({url: '#{url_for(url)}', type: 'get'}); return false;"
link_to text, '#', options.merge(:onclick => onclick)
diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb
index c8e8bd87b..34c6dad15 100644
--- a/app/helpers/members_helper.rb
+++ b/app/helpers/members_helper.rb
@@ -1,96 +1,123 @@
-# encoding: utf-8
-#
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-module MembersHelper
- def render_principals_for_new_members(project)
- scope = Principal.active.sorted.not_member_of(project).like(params[:q])
- principal_count = scope.count
- principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page'] #by young
- principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
- s = content_tag('div', principals_check_box_tags_ex('membership[user_ids][]', principals), :id => 'principals')
- links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
- link_to text, autocomplete_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
- }
- s + content_tag('div', content_tag('ul', links), :class => 'pagination_new')
- end
-
- #获取项目可邀请的成员列表
- def render_project_members project
- scope = Principal.active.sorted.not_member_of(project).like(params[:q])
- principals = paginateHelper scope,10
- s = content_tag('ul', project_member_check_box_tags_ex('membership[user_ids][]', principals), :class => 'mb5', :style => "margin-left: -40px;")
- links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true){|text, parameters, options|
- link_to text, autocomplete_project_memberships_path(project, parameters.merge(:q => params[:q],:flag => true, :format => 'js')), :remote => true
- }
- s + content_tag('ul', links,:class => 'wlist')
- end
-
- # add by nwb
- # 课程可添加的成员列表
- def render_principals_for_new_course_members(course)
- if params[:q] && params[:q] != ""
- scope = Principal.active.sorted.not_member_of_course(course).like(params[:q])
- else
- scope = []
- end
- principals = paginateHelper scope,10
- s = content_tag('ul', project_member_check_box_tags_ex('membership[user_ids][]', principals), :class => 'mb5', :id => 'principals')
-
- links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true) {|text, parameters, options|
- link_to text, autocomplete_course_memberships_path(course, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
- }
-
- s + content_tag('ul', links,:class => 'wlist',:id => "course_member_pagination_links")
- end
-
-
- # 当前申请加入的成员名单
- def render_principals_for_applied_members(project)
- scope = project.applied_projects.map(&:user)
- principal_count = scope.count
- principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page']
- offset ||= principal_pages.offset
- principals = scope[offset, 10]
- #principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
- #principals = ApplicationController.new.paginateHelper scope,10
-
- s = content_tag('div', principals_check_box_tags_ex('membership[user_ids][]', principals), :id => 'principals')
-
- links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
- link_to text, appliedproject_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
- }
-
- s + content_tag('div', content_tag('ul', links), :class => 'applied_new')
- end
-
- private
- def paginateHelper obj, pre_size=20
- @obj_count = obj.count
- @obj_pages = Redmine::Pagination::Paginator.new @obj_count, pre_size, params['page']
- if obj.kind_of? ActiveRecord::Base or obj.kind_of? ActiveRecord::Relation
- obj.limit(@obj_pages.per_page).offset(@obj_pages.offset)
- elsif obj.kind_of? Array
- obj[@obj_pages.offset, @obj_pages.per_page]
- else
- logger.error "[ApplicationController] Error : application_controller#paginateHelper ===> unknow category: #{obj.class}"
- raise RuntimeError, 'unknow type, Please input you type into this helper.'
- end
- end
-
-end
+# encoding: utf-8
+#
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+module MembersHelper
+ def render_principals_for_new_members(project)
+ scope = Principal.active.sorted.not_member_of(project).like(params[:q])
+ principal_count = scope.count
+ principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page'] #by young
+ principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
+ s = content_tag('div', principals_check_box_tags_ex('membership[user_ids][]', principals), :id => 'principals')
+ links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
+ link_to text, autocomplete_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
+ }
+ s + content_tag('div', content_tag('ul', links), :class => 'pagination_new')
+ end
+
+ #获取项目可邀请的成员列表
+ def render_project_members project
+ if params[:q] && params[:q] != ""
+ scope = Principal.active.sorted.not_member_of(project).like(params[:q])
+ else
+ scope = []
+ end
+ principals = paginateHelper scope,10
+ s = content_tag('ul', project_member_check_box_tags_ex('membership[user_ids][]', principals), :class => 'mb5')
+ links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true){|text, parameters, options|
+ link_to text, autocomplete_project_memberships_path(project, parameters.merge(:q => params[:q],:flag => true, :format => 'js')), :remote => true
+ }
+ s + content_tag('ul', links,:class => 'wlist', :id => "course_member_pagination_links" )
+ end
+
+ # add by nwb
+ # 课程可添加的成员列表
+ def render_principals_for_new_course_members(course)
+ if params[:q] && params[:q] != ""
+ scope = Principal.active.sorted.not_member_of_course(course).like(params[:q])
+ else
+ scope = []
+ end
+ principals = paginateHelper scope,10
+ s = content_tag('ul', project_member_check_box_tags_ex('membership[user_ids][]', principals), :class => 'mb5', :id => 'principals')
+
+ links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true) {|text, parameters, options|
+ link_to text, autocomplete_course_memberships_path(course, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
+ }
+
+ s + content_tag('ul', links,:class => 'wlist',:id => "course_member_pagination_links")
+ end
+
+ # 项目配置中添加成员列表
+ def render_principals_for_new_project_members(project)
+ scope = Principal.active.sorted.not_member_of(project).like(params[:q])
+ principals = paginateHelper scope,10
+ s = content_tag('ul', project_member_check_box_tags_ex('membership[user_ids][]', principals), :class => 'mb5', :id => 'principals')
+
+ links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true) {|text, parameters, options|
+ link_to text, appliedproject_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
+ }
+
+ s + content_tag('ul', links,:class => 'wlist',:id => "course_member_pagination_links")
+ end
+
+ # 新申请加入项目成员列表
+ def render_principals_for_applied_members_new project
+ scope = project.applied_projects.map(&:user)
+ principals = paginateHelper scope,10
+ s = content_tag('ul', principals_check_box_tags_li('membership[user_ids][]', principals), :class => 'mb5')
+ links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true){|text, parameters, options|
+ link_to text, appliedproject_project_memberships_path(project, parameters.merge(:q => params[:q],:flag => true, :format => 'js')), :remote => true
+ }
+ s + content_tag('ul', links,:class => 'wlist', :id => "course_member_pagination_links" )
+ end
+
+ # 当前申请加入的成员名单
+ def render_principals_for_applied_members(project)
+ scope = project.applied_projects.map(&:user)
+ principal_count = scope.count
+ principal_pages = Redmine::Pagination::Paginator.new principal_count, 10, params['page']
+ offset ||= principal_pages.offset
+ principals = scope[offset, 10]
+ #principals = scope.offset(principal_pages.offset).limit(principal_pages.per_page).all
+ #principals = ApplicationController.new.paginateHelper scope,10
+
+ s = content_tag('div', principals_check_box_tags_ex('membership[user_ids][]', principals), :id => 'principals')
+
+ links = pagination_links_full(principal_pages, principal_count, :per_page_links => false) {|text, parameters, options|
+ link_to text, appliedproject_project_memberships_path(project, parameters.merge(:q => params[:q], :format => 'js')), :remote => true
+ }
+
+ s + content_tag('div', content_tag('ul', links), :class => 'applied_new')
+ end
+
+ private
+ def paginateHelper obj, pre_size=20
+ @obj_count = obj.count
+ @obj_pages = Redmine::Pagination::Paginator.new @obj_count, pre_size, params['page']
+ if obj.kind_of? ActiveRecord::Base or obj.kind_of? ActiveRecord::Relation
+ obj.limit(@obj_pages.per_page).offset(@obj_pages.offset)
+ elsif obj.kind_of? Array
+ obj[@obj_pages.offset, @obj_pages.per_page]
+ else
+ logger.error "[ApplicationController] Error : application_controller#paginateHelper ===> unknow category: #{obj.class}"
+ raise RuntimeError, 'unknow type, Please input you type into this helper.'
+ end
+ end
+
+end
diff --git a/app/helpers/owner_type_helper.rb b/app/helpers/owner_type_helper.rb
index 98f273bee..dd5dbbbac 100644
--- a/app/helpers/owner_type_helper.rb
+++ b/app/helpers/owner_type_helper.rb
@@ -1,9 +1,9 @@
-module OwnerTypeHelper
- MEMO = 1
- FORUM = 2
- MESSAGE = 3
- NEWS = 4
- COMMENT = 5
- BID = 6
- JOURNALSFORMESSAGE = 7
+module OwnerTypeHelper
+ MEMO = 1
+ FORUM = 2
+ MESSAGE = 3
+ NEWS = 4
+ COMMENT = 5
+ BID = 6
+ JOURNALSFORMESSAGE = 7
end
\ No newline at end of file
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 3c1663fcb..508e58ba3 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -21,7 +21,12 @@ include AvatarHelper
module ProjectsHelper
def link_to_version(version, options = {})
return '' unless version && version.is_a?(Version)
- link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, options
+ link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, :class => "c_blue02"
+ end
+
+ def link_to_version_show(version, options = {})
+ return '' unless version && version.is_a?(Version)
+ link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, :class => " f16 fb c_dblue "
end
def project_settings_tabs
@@ -29,7 +34,7 @@ module ProjectsHelper
{:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
{:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
{:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
- {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
+ # {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
# {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
{:name => 'repositories', :action => :manage_repository, :partial => 'projects/settings/repositories', :label => :label_repository_plural},
#{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
@@ -386,4 +391,20 @@ module ProjectsHelper
end
type
end
+
+ #显示项目配置菜单
+ def show_project_memu user
+ if user.allowed_to?(:edit_project, @project)
+ result = "edit_project"
+ elsif user.allowed_to?(:select_project_modules, @project)
+ result = "select_project_modules"
+ elsif user.allowed_to?(:manage_members, @project)
+ result = "manage_members"
+ elsif user.allowed_to?(:manage_versions, @project)
+ result = "manage_versions"
+ elsif user.allowed_to?(:manage_repository, @project)
+ result = "manage_repository"
+ end
+ result
+ end
end
diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb
index f36d2ea94..e1d8c3e12 100644
--- a/app/helpers/queries_helper.rb
+++ b/app/helpers/queries_helper.rb
@@ -255,8 +255,19 @@ module QueriesHelper
# Give it a name, required to be valid
@query = IssueQuery.new(:name => "_")
@query.project = @project
+ params[:f] = %w(subject status_id priority_id author_id assigned_to_id) unless params[:status_id].nil?
+ params[:op] = {'subject' => "~" ,
+ 'status_id' => ( params[:status_id] == '0' ? "!":"=" ),
+ 'priority_id' => ( params[:priority_id] == '0' ? "!":"=" ),
+ 'author_id' => ( params[:author_id] == '0' ? "!":"=" ),
+ 'assigned_to_id' => ( params[:assigned_to_id] == '0' ? "!":"=" )} unless params[:status_id].nil?
+ params[:v] = {'subject' => [params[:subject]],
+ 'status_id' => [params[:status_id]],
+ 'priority_id' => [params[:priority_id]],
+ 'author_id' => [params[:author_id]],
+ 'assigned_to_id' => [params[:assigned_to_id]]} unless params[:status_id].nil?
@query.build_from_params(params)
- session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
+ #session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
else
# retrieve from session
@query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
diff --git a/app/helpers/watchers_helper.rb b/app/helpers/watchers_helper.rb
index 7995d7e68..d3b2a49b1 100644
--- a/app/helpers/watchers_helper.rb
+++ b/app/helpers/watchers_helper.rb
@@ -42,7 +42,28 @@ module WatchersHelper
:object_id => (objects.size == 1 ? objects.first.id : objects.map(&:id).sort)
)
method = watched ? 'delete' : 'post'
-
+
+ link_to text, url, :remote => true, :method => method, :class => css
+ end
+
+ def watcher_link_issue(objects, user, options=[])
+ return '' unless user && user.logged?
+ objects = Array.wrap(objects)
+
+ watched = objects.any? {|object| object.watched_by?(user)}
+ @watch_flag = (objects.first.instance_of?(User) or objects.first.instance_of?(Project) or objects.first.instance_of?(Contest) or (objects.first.instance_of?(Bid)))
+ css = @watch_flag ? ([watcher_css(objects), watched ? 'talk_edit ' : 'talk_edit '].join(' ') << options[0].to_s) :
+ ([watcher_css(objects), watched ? 'talk_edit fr ' : 'talk_edit fr '].join(' ') << options[0].to_s)
+
+ text = @watch_flag ?
+ (watched ? l(:button_unfollow) : l(:button_follow)) : (watched ? l(:button_unwatch) : l(:button_watch))
+
+ url = watch_path(
+ :object_type => objects.first.class.to_s.underscore,
+ :object_id => (objects.size == 1 ? objects.first.id : objects.map(&:id).sort)
+ )
+ method = watched ? 'delete' : 'post'
+
link_to text, url, :remote => true, :method => method, :class => css
end
@@ -261,17 +282,36 @@ module WatchersHelper
content.present? ? content_tag('ul', content, :class => 'watchers') : content
end
+
+
+
+
+
+
+# 缺陷跟踪者列表复选框生成
def watchers_checkboxes(object, users, checked=nil)
if users.nil?
else
+ # tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
+ # content_tag 'label', "#{tag} #{h(user)}".html_safe,
+ # :id => "issue_watcher_user_ids_#{user.id}",
+ # :class => "floating"
users.map do |user|
c = checked.nil? ? object.watched_by?(user) : checked
- tag = check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil
- content_tag 'label', "#{tag} #{h(user)}".html_safe,
- :id => "issue_watcher_user_ids_#{user.id}",
- :class => "floating"
+ s = content_tag(:ul,
+ content_tag(:li, "#{check_box_tag 'issue[watcher_user_ids][]', user.id, c, :id => nil } #{h link_to user.userInfo, user_path( user.id)}".html_safe,
+ :id=>"issue_watcher_user_ids_#{user.id}",:style=>"float: left;width: 175px;margin: 0px 20px 10px 0px; overflow: hidden; line-height:1.6em;" ),
+ :class => "mb10 ml80")
end.join.html_safe
+
+ # scope = users.sort
+ # watchers = paginateHelper scope,10
+ # s = content_tag('ul', issue_watcher_check_box_tags_ex('issue[watcher_user_ids][]', watchers), :class => 'mb10 ml80')
+ # links = pagination_links_full(@obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true){|text, parameters, options|
+ # link_to text, watchers_autocomplete_for_user_path(@users, parameters.merge(:q => params[:q],:format => 'js',:flag => 'ture')), :remote => true
+ # }
+ # s + content_tag('ul', links,:class => 'wlist', :style =>"float:left;margin-top:0px;")
end
end
@@ -304,8 +344,8 @@ module WatchersHelper
def exit_project_link(project)
link_to(l(:label_exit_project),exit_cur_project_path(project.id),
:remote => true, :confirm => l(:lable_sure_exit_project),
- :style => "color: #fff; display:block;font-size:12px; padding: 0px 5px; margin-right: 10px; height: 20px; line-height: 22px; background: none repeat scroll 0% 0% #64BDD9; TES;padding-top:1px;" )
- end
+ :class => "pr_join_a_quit" )
+ end
#项目关注、取消关注
#REDO:项目样式确定后方法需要对CSS变量进行改进
@@ -321,7 +361,7 @@ module WatchersHelper
:object_id => (objects.size == 1 ? objects.first.id : objects.map(&:id).sort))
method = watched ? 'delete' : 'post'
link_to text, url, :remote => true, :method => method,
- :class => "project_watch_new" ,:id=>id
+ :class => "pr_join_a" ,:id=>id
end
#申请加入项目
@@ -339,7 +379,21 @@ module WatchersHelper
:user_id => user.id,
:project_id => project.id)
method = applied ? 'delete' : 'post'
- link_to text, url, :remote => true, :method => method , :class => "project_watch_new",:id => id
+ link_to text, url, :remote => true, :method => method , :class => "pr_join_a",:id => id
+ end
+
+ def paginateHelper obj, pre_size=20
+ @obj_count = obj.count
+ @obj_pages = Redmine::Pagination::Paginator.new @obj_count, pre_size, params['page']
+ if obj.kind_of? ActiveRecord::Base or obj.kind_of? ActiveRecord::Relation
+ obj.limit(@obj_pages.per_page).offset(@obj_pages.offset)
+ elsif obj.kind_of? Array
+ obj[@obj_pages.offset, @obj_pages.per_page]
+ else
+ logger.error "[ApplicationController] Error : application_controller#paginateHelper ===> unknow category: #{obj.class}"
+ raise RuntimeError, 'unknow type, Please input you type into this helper.'
end
+ end
+
end
diff --git a/app/models/document.rb b/app/models/document.rb
index c8e5f8a24..6bfb4b8ff 100644
--- a/app/models/document.rb
+++ b/app/models/document.rb
@@ -1,95 +1,95 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class Document < ActiveRecord::Base
- include Redmine::SafeAttributes
- belongs_to :project
- belongs_to :user
- belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
- include UserScoreHelper
- after_save :be_user_score # user_score
- after_destroy :down_user_score
- acts_as_attachable :delete_permission => :delete_documents
- after_create :send_mail
- # 被ForgeActivity虚拟关联
- has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
- # end
- acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
- acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
- #:author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
- :author => Proc.new {|o| User.find(o.user_id)},
- :url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
- acts_as_activity_provider :find_options => {:include => :project},
- :is_public => 'documents.is_public'
-
- validates_presence_of :project, :title, :category
- validates_length_of :title, :maximum => 60
- after_create :act_as_forge_activity
- scope :visible, lambda {|*args|
- includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
- }
-
- safe_attributes 'category_id', 'title', 'description','is_public'
-
- def visible?(user=User.current)
- !user.nil? && user.allowed_to?(:view_documents, project)
- end
-
- def has_right?(project,user=User.current)
- user.admin? || user.member_of?(project) || self.is_public==1
- end
-
- def initialize(attributes=nil, *args)
- super
- if new_record?
- self.category ||= DocumentCategory.default
- end
- end
-
- def updated_on
- unless @updated_on
- a = attachments.last
- @updated_on = (a && a.created_on) || created_on
- end
- @updated_on
- end
-
- # update user score
- def be_user_score
- UserScore.project(:push_document, self.user,self,{ document_id: self.id })
- update_document(self.user,1)
- update_document(self.user,2,self.project)
- end
-
- def down_user_score
- update_document(self.user,1)
- update_document(self.user,2,self.project)
- end
-
- # Time 2015-03-02 10:51:16
- # Author lizanle
- # Description 新创建的document要在公共表ForgeActivity中记录
- def act_as_forge_activity
- self.forge_acts << ForgeActivity.new(:user_id => self.user_id,
- :project_id => self.project_id)
- end
-
- def send_mail
- Mailer.run.document_added(self) if Setting.notified_events.include?('document_added')
- end
-
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Document < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ belongs_to :project
+ belongs_to :user
+ belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id"
+ include UserScoreHelper
+ after_save :be_user_score # user_score
+ after_destroy :down_user_score
+ acts_as_attachable :delete_permission => :delete_documents
+ after_create :send_mail
+ # 被ForgeActivity虚拟关联
+ has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
+ # end
+ acts_as_searchable :columns => ['title', "#{table_name}.description"], :include => :project
+ acts_as_event :title => Proc.new {|o| "#{l(:label_document)}: #{o.title}"},
+ #:author => Proc.new {|o| o.attachments.reorder("#{Attachment.table_name}.created_on ASC").first.try(:author) },
+ :author => Proc.new {|o| User.find(o.user_id)},
+ :url => Proc.new {|o| {:controller => 'documents', :action => 'show', :id => o.id}}
+ acts_as_activity_provider :find_options => {:include => :project},
+ :is_public => 'documents.is_public'
+
+ validates_presence_of :project, :title, :category
+ validates_length_of :title, :maximum => 60
+ after_create :act_as_forge_activity
+ scope :visible, lambda {|*args|
+ includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_documents, *args))
+ }
+
+ safe_attributes 'category_id', 'title', 'description','is_public'
+
+ def visible?(user=User.current)
+ !user.nil? && user.allowed_to?(:view_documents, project)
+ end
+
+ def has_right?(project,user=User.current)
+ user.admin? || user.member_of?(project) || self.is_public==1
+ end
+
+ def initialize(attributes=nil, *args)
+ super
+ if new_record?
+ self.category ||= DocumentCategory.default
+ end
+ end
+
+ def updated_on
+ unless @updated_on
+ a = attachments.last
+ @updated_on = (a && a.created_on) || created_on
+ end
+ @updated_on
+ end
+
+ # update user score
+ def be_user_score
+ UserScore.project(:push_document, self.user,self,{ document_id: self.id })
+ update_document(self.user,1)
+ update_document(self.user,2,self.project)
+ end
+
+ def down_user_score
+ update_document(self.user,1)
+ update_document(self.user,2,self.project)
+ end
+
+ # Time 2015-03-02 10:51:16
+ # Author lizanle
+ # Description 新创建的document要在公共表ForgeActivity中记录
+ def act_as_forge_activity
+ self.forge_acts << ForgeActivity.new(:user_id => self.user_id,
+ :project_id => self.project_id)
+ end
+
+ def send_mail
+ Mailer.run.document_added(self) if Setting.notified_events.include?('document_added')
+ end
+
+end
diff --git a/app/models/forum.rb b/app/models/forum.rb
index 6db069a09..2af1abf9e 100644
--- a/app/models/forum.rb
+++ b/app/models/forum.rb
@@ -1,58 +1,58 @@
-class Forum < ActiveRecord::Base
- include Redmine::SafeAttributes
- include ApplicationHelper
- has_many_kindeditor_assets :assets, :dependent => :destroy
- has_many :topics, :class_name => 'Memo', :conditions => "#{Memo.table_name}.parent_id IS NULL", :order => "#{Memo.table_name}.created_at DESC", :dependent => :destroy
- has_many :memos, :dependent => :destroy, conditions: "parent_id IS NULL"
- belongs_to :creator, :class_name => "User", :foreign_key => 'creator_id'
- safe_attributes 'name',
- 'description',
- 'topic_count',
- 'memo_count',
- 'last_memo_id',
- 'creator_id',
- 'sticky',
- 'locked'
- validates_presence_of :name, :creator_id, :description
- validates_length_of :name, maximum: 50
- #validates_length_of :description, maximum: 255
- validates :name, :uniqueness => true
- after_destroy :delete_kindeditor_assets
- acts_as_taggable
- scope :by_join_date, order("created_at DESC")
- after_create :send_mail
- def reset_counters!
- self.class.reset_counters!(id)
- end
-
- def editable_by? user
- # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
- self.creator == user || user.admin?
- end
-
- def destroyable_by? user
- # user && user.logged? && Forum.find(self.forum_id).creator_id == user.id || user.admin?
- self.creator == user || user.admin?
- end
-
- def send_mail
- logger.debug "send mail for forum add."
- Mailer.run.forum_add(self) if Setting.notified_events.include?('forum_add')
- end
- # Updates topic_count, memo_count and last_memo_id attributes for +board_id+
- def self.reset_counters!(forum_id)
- forum_id = forum_id.to_i
- update_all("topic_count = (SELECT COUNT(*) FROM #{Memo.table_name} WHERE forum_id=#{forum_id} AND parent_id IS NULL)," +
- " memo_count = (SELECT COUNT(*) FROM #{Memo.table_name} WHERE forum_id=#{forum_id} AND parent_id IS NOT NULL)," +
- " last_memo_id = (SELECT MAX(id) FROM #{Memo.table_name} WHERE forum_id=#{forum_id})",
- ["id = ?", forum_id])
- end
-
- # Time 2015-03-26 15:50:54
- # Author lizanle
- # Description 删除论坛后删除对应的资源
- def delete_kindeditor_assets
- delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::FORUM
- end
-
-end
+class Forum < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ include ApplicationHelper
+ has_many_kindeditor_assets :assets, :dependent => :destroy
+ has_many :topics, :class_name => 'Memo', :conditions => "#{Memo.table_name}.parent_id IS NULL", :order => "#{Memo.table_name}.created_at DESC", :dependent => :destroy
+ has_many :memos, :dependent => :destroy, conditions: "parent_id IS NULL"
+ belongs_to :creator, :class_name => "User", :foreign_key => 'creator_id'
+ safe_attributes 'name',
+ 'description',
+ 'topic_count',
+ 'memo_count',
+ 'last_memo_id',
+ 'creator_id',
+ 'sticky',
+ 'locked'
+ validates_presence_of :name, :creator_id, :description
+ validates_length_of :name, maximum: 50
+ #validates_length_of :description, maximum: 255
+ validates :name, :uniqueness => true
+ after_destroy :delete_kindeditor_assets
+ acts_as_taggable
+ scope :by_join_date, order("created_at DESC")
+ after_create :send_mail
+ def reset_counters!
+ self.class.reset_counters!(id)
+ end
+
+ def editable_by? user
+ # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
+ self.creator == user || user.admin?
+ end
+
+ def destroyable_by? user
+ # user && user.logged? && Forum.find(self.forum_id).creator_id == user.id || user.admin?
+ self.creator == user || user.admin?
+ end
+
+ def send_mail
+ logger.debug "send mail for forum add."
+ Mailer.run.forum_add(self) if Setting.notified_events.include?('forum_add')
+ end
+ # Updates topic_count, memo_count and last_memo_id attributes for +board_id+
+ def self.reset_counters!(forum_id)
+ forum_id = forum_id.to_i
+ update_all("topic_count = (SELECT COUNT(*) FROM #{Memo.table_name} WHERE forum_id=#{forum_id} AND parent_id IS NULL)," +
+ " memo_count = (SELECT COUNT(*) FROM #{Memo.table_name} WHERE forum_id=#{forum_id} AND parent_id IS NOT NULL)," +
+ " last_memo_id = (SELECT MAX(id) FROM #{Memo.table_name} WHERE forum_id=#{forum_id})",
+ ["id = ?", forum_id])
+ end
+
+ # Time 2015-03-26 15:50:54
+ # Author lizanle
+ # Description 删除论坛后删除对应的资源
+ def delete_kindeditor_assets
+ delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::FORUM
+ end
+
+end
diff --git a/app/models/issue_observer.rb b/app/models/issue_observer.rb
index 51f64c783..ea78349af 100644
--- a/app/models/issue_observer.rb
+++ b/app/models/issue_observer.rb
@@ -1,28 +1,28 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class IssueObserver < ActiveRecord::Observer
-
- def after_create(issue)
- # 将跟踪者与本项目的其他成员都设为收件方,并去重,不在进行抄送,
- recipients = issue.recipients - issue.watcher_recipients + issue.watcher_recipients
- recipients.each do |rec|
- Mailer.run.issue_add(issue,rec) if Setting.notified_events.include?('issue_added')
- end
-
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class IssueObserver < ActiveRecord::Observer
+
+ def after_create(issue)
+ # 将跟踪者与本项目的其他成员都设为收件方,并去重,不在进行抄送,
+ recipients = issue.recipients - issue.watcher_recipients + issue.watcher_recipients
+ recipients.each do |rec|
+ Mailer.run.issue_add(issue,rec) if Setting.notified_events.include?('issue_added')
+ end
+
+ end
+end
diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb
index c55143ca8..dba45cb43 100644
--- a/app/models/issue_query.rb
+++ b/app/models/issue_query.rb
@@ -310,7 +310,7 @@ class IssueQuery < Query
:order => options[:order],
:limit => options[:limit],
:offset => options[:offset]
- )
+ ).reverse
rescue ::ActiveRecord::StatementInvalid => e
raise StatementInvalid.new(e.message)
end
diff --git a/app/models/journal_observer.rb b/app/models/journal_observer.rb
index 1fca58f37..c5b0e496b 100644
--- a/app/models/journal_observer.rb
+++ b/app/models/journal_observer.rb
@@ -1,34 +1,34 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class JournalObserver < ActiveRecord::Observer
- def after_create(journal)
- if journal.notify? &&
- (Setting.notified_events.include?('issue_updated') ||
- (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
- (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
- (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
- )
- # 将跟踪者与本项目的其他成员都设为收件方,并去重,不在进行抄送,
- recipients = journal.recipients - journal.watcher_recipients + journal.watcher_recipients
- recipients.each do |rec|
-
- Mailer.run.issue_edit(journal,rec)
- end
- end
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class JournalObserver < ActiveRecord::Observer
+ def after_create(journal)
+ if journal.notify? &&
+ (Setting.notified_events.include?('issue_updated') ||
+ (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) ||
+ (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) ||
+ (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?)
+ )
+ # 将跟踪者与本项目的其他成员都设为收件方,并去重,不在进行抄送,
+ recipients = journal.recipients - journal.watcher_recipients + journal.watcher_recipients
+ recipients.each do |rec|
+
+ Mailer.run.issue_edit(journal,rec)
+ end
+ end
+ end
+end
diff --git a/app/models/journals_for_message.rb b/app/models/journals_for_message.rb
index dda45965e..396501862 100644
--- a/app/models/journals_for_message.rb
+++ b/app/models/journals_for_message.rb
@@ -1,175 +1,175 @@
-# fq
-# 数据库字段中带有m前缀和is_readed是二次开发添加,之前的字段基本复用
-# 注意reply_id 是提到人的id,不是留言id, Base中叫做 at_user
-class JournalsForMessage < ActiveRecord::Base
- include Redmine::SafeAttributes
- include UserScoreHelper
- include ApplicationHelper
- has_many_kindeditor_assets :assets, :dependent => :destroy
- safe_attributes "jour_type", # 留言所属类型
- "jour_id", # 留言所属类型的id
- "notes", # 留言内容
- "reply_id", # 留言被回复留言者的用户id(用户a回复了用户b,这是b的id,用以查询谁给b留言了)
- "status", # 留言是否被查看(弃用)
- "user_id", # 留言者的id
- "m_parent_id", # 留言信息的父留言id
- "is_readed", # 留言是否已读
- "m_reply_count", # 留言的回复数量
- "m_reply_id" # 回复某留言的留言id(a留言回复了b留言,这是b留言的id)
- "is_comprehensive_evaluation" # 1 教师评论、2 匿评、3 留言
- acts_as_tree :foreign_key => 'm_parent_id', :counter_cache => :m_reply_count, :order => "#{JournalsForMessage.table_name}.created_on ASC"
- after_destroy :delete_kindeditor_assets
- belongs_to :project,
- :foreign_key => 'jour_id',
- :conditions => "#{self.table_name}.jour_type = 'Project' "
- belongs_to :course,
- :foreign_key => 'jour_id'
-
-
- belongs_to :jour, :polymorphic => true
- belongs_to :user
- belongs_to :homework_attach
- belongs_to :at_user, :class_name => "User", :foreign_key => 'reply_id'
-
- acts_as_event :title => Proc.new {|o| "#{l(:label_my_message)}"},
- :datetime => Proc.new {|o| o.updated_on },
- :author => Proc.new {|o| o.user },
- :description => Proc.new{|o| o.notes },
- :type => Proc.new {|o| o.jour_type },
- :url => Proc.new {|o|
- if o.jour.kind_of? Project
- {:controller => 'projects', :action => 'feedback', :id => o.jour, :r => o.id, :anchor => "word_li_#{o.id}"}
- elsif o.jour.kind_of? Course
- {:controller => 'courses', :action => 'feedback', :id => o.jour, :r => o.id, :anchor => "word_li_#{o.id}"}
- end
- }
- acts_as_activity_provider :author_key => :user_id,
- :timestamp => "#{self.table_name}.updated_on",
- :find_options => {:include => :project }
-
- acts_as_activity_provider :type => 'course_journals_for_messages',
- :author_key => :user_id,
- :permission => :view_course_journals_for_messages,
- :timestamp => "#{self.table_name}.updated_on",
- :find_options => {:include => :course }
-
-
- has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
-
- validates :notes, presence: true
- after_create :act_as_activity #huang
- after_create :reset_counters!
- after_destroy :reset_counters!
- after_save :be_user_score
- after_destroy :down_user_score
-
- # default_scope { where('m_parent_id IS NULL') }
-
- def self.create_by_user? user
- if user.anonymous?
- return false
- else
- return true
- end
- end
-
-
- def self.remove_by_user? user
- if( self.user == user ||
- ( self.jour.kind_of?(User) && self.jour== user )
- )
- true
- else
- false
- end
- end
-
- def self.delete_message(message_id)
- self.find(message_id).destroy
- # self.destroy_all "id = #{message_id}"
- end
-
- def reference_user
- User.find(reply_id)
- end
-
- def delete_by_user?user
- # 用户可删除自己的留言
- if self.user.id == user.id || user.admin?
- return true
- else
- return false
- end
- end
-
- def self.reference_message(user_id)
- @user = User.find(user_id)
- message = JournalsForMessage.find_by_sql("select * from journals_for_messages where reply_id = #{@user.id}
- or (jour_type = 'Bid' and jour_id in (select id from bids where author_id = #{@user.id}))")
- message
- end
-
- def act_as_activity
- if self.jour_type == 'Principal'
- unless self.user_id == self.jour.id && self.user_id != self.reply_id && self.reply_id != 0
- # self.acts << Activity.new(:user_id => self.user_id)
- self.acts << Activity.new(:user_id => self.jour_id)
- end
- elsif self.jour_type == 'Project'
- self.acts << Activity.new(:user_id => self.reply_id)
- elsif self.jour_type == 'Course'
- self.acts << Activity.new(:user_id => self.reply_id)
- else
- end
- end
-
- def reset_counters!
- self.class.reset_counters!(self)
- end
- def self.reset_counters! journals_for_messages
- # jfm_id = journals_for_messages.id.to_i
- count = find_all_by_m_parent_id(journals_for_messages.m_parent_id).count #(SELECT COUNT(*) FROM #{JournalsForMessage.table_name} WHERE m_parent_id = #{jfm_id} )
- update_all("m_reply_count = #{count.to_i}", ["id = ?", journals_for_messages.m_parent_id])
- end
-
- #如果是在项目中留言则返回该项目否则返回nil - zjc
- def project
- if self.jour_type == 'Project'
- Project.find(self.jour_id)
- else
- nil
- end
- end
-
- # 更新用户分数 -by zjc
- def be_user_score
- #新建了留言回复
- if self.reply_id != 0
- #协同得分加分
- UserScore.joint(:reply_message, self.user,User.find(self.reply_id),self, { journals_for_messages_id: self.id })
- update_replay_for_message(self.user,1)
- if self.jour_type == "Project"
- update_replay_for_message(self.user,2,self.jour)
- end
- end
- end
- # 更新用户分数 -by zjc
- def down_user_score
- #删除了留言回复
- if self.reply_id != 0
- #协同得分减分
- UserScore.joint(:reply_message_delete, self.user,User.find(self.reply_id), { journals_for_messages_id: self.id })
- update_replay_for_message(self.user,1)
- if self.jour_type == "Project"
- update_replay_for_message(self.user,2,self.jour)
- end
- end
- end
-
- # Time 2015-04-01 14:15:06
- # Author lizanle
- # Description 删除对应课程留言的图片资源
- def delete_kindeditor_assets
- delete_kindeditor_assets_from_disk self.id,7
- end
-end
+# fq
+# 数据库字段中带有m前缀和is_readed是二次开发添加,之前的字段基本复用
+# 注意reply_id 是提到人的id,不是留言id, Base中叫做 at_user
+class JournalsForMessage < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ include UserScoreHelper
+ include ApplicationHelper
+ has_many_kindeditor_assets :assets, :dependent => :destroy
+ safe_attributes "jour_type", # 留言所属类型
+ "jour_id", # 留言所属类型的id
+ "notes", # 留言内容
+ "reply_id", # 留言被回复留言者的用户id(用户a回复了用户b,这是b的id,用以查询谁给b留言了)
+ "status", # 留言是否被查看(弃用)
+ "user_id", # 留言者的id
+ "m_parent_id", # 留言信息的父留言id
+ "is_readed", # 留言是否已读
+ "m_reply_count", # 留言的回复数量
+ "m_reply_id" # 回复某留言的留言id(a留言回复了b留言,这是b留言的id)
+ "is_comprehensive_evaluation" # 1 教师评论、2 匿评、3 留言
+ acts_as_tree :foreign_key => 'm_parent_id', :counter_cache => :m_reply_count, :order => "#{JournalsForMessage.table_name}.created_on ASC"
+ after_destroy :delete_kindeditor_assets
+ belongs_to :project,
+ :foreign_key => 'jour_id',
+ :conditions => "#{self.table_name}.jour_type = 'Project' "
+ belongs_to :course,
+ :foreign_key => 'jour_id'
+
+
+ belongs_to :jour, :polymorphic => true
+ belongs_to :user
+ belongs_to :homework_attach
+ belongs_to :at_user, :class_name => "User", :foreign_key => 'reply_id'
+
+ acts_as_event :title => Proc.new {|o| "#{l(:label_my_message)}"},
+ :datetime => Proc.new {|o| o.updated_on },
+ :author => Proc.new {|o| o.user },
+ :description => Proc.new{|o| o.notes },
+ :type => Proc.new {|o| o.jour_type },
+ :url => Proc.new {|o|
+ if o.jour.kind_of? Project
+ {:controller => 'projects', :action => 'feedback', :id => o.jour, :r => o.id, :anchor => "word_li_#{o.id}"}
+ elsif o.jour.kind_of? Course
+ {:controller => 'courses', :action => 'feedback', :id => o.jour, :r => o.id, :anchor => "word_li_#{o.id}"}
+ end
+ }
+ acts_as_activity_provider :author_key => :user_id,
+ :timestamp => "#{self.table_name}.updated_on",
+ :find_options => {:include => :project }
+
+ acts_as_activity_provider :type => 'course_journals_for_messages',
+ :author_key => :user_id,
+ :permission => :view_course_journals_for_messages,
+ :timestamp => "#{self.table_name}.updated_on",
+ :find_options => {:include => :course }
+
+
+ has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
+
+ validates :notes, presence: true
+ after_create :act_as_activity #huang
+ after_create :reset_counters!
+ after_destroy :reset_counters!
+ after_save :be_user_score
+ after_destroy :down_user_score
+
+ # default_scope { where('m_parent_id IS NULL') }
+
+ def self.create_by_user? user
+ if user.anonymous?
+ return false
+ else
+ return true
+ end
+ end
+
+
+ def self.remove_by_user? user
+ if( self.user == user ||
+ ( self.jour.kind_of?(User) && self.jour== user )
+ )
+ true
+ else
+ false
+ end
+ end
+
+ def self.delete_message(message_id)
+ self.find(message_id).destroy
+ # self.destroy_all "id = #{message_id}"
+ end
+
+ def reference_user
+ User.find(reply_id)
+ end
+
+ def delete_by_user?user
+ # 用户可删除自己的留言
+ if self.user.id == user.id || user.admin?
+ return true
+ else
+ return false
+ end
+ end
+
+ def self.reference_message(user_id)
+ @user = User.find(user_id)
+ message = JournalsForMessage.find_by_sql("select * from journals_for_messages where reply_id = #{@user.id}
+ or (jour_type = 'Bid' and jour_id in (select id from bids where author_id = #{@user.id}))")
+ message
+ end
+
+ def act_as_activity
+ if self.jour_type == 'Principal'
+ unless self.user_id == self.jour.id && self.user_id != self.reply_id && self.reply_id != 0
+ # self.acts << Activity.new(:user_id => self.user_id)
+ self.acts << Activity.new(:user_id => self.jour_id)
+ end
+ elsif self.jour_type == 'Project'
+ self.acts << Activity.new(:user_id => self.reply_id)
+ elsif self.jour_type == 'Course'
+ self.acts << Activity.new(:user_id => self.reply_id)
+ else
+ end
+ end
+
+ def reset_counters!
+ self.class.reset_counters!(self)
+ end
+ def self.reset_counters! journals_for_messages
+ # jfm_id = journals_for_messages.id.to_i
+ count = find_all_by_m_parent_id(journals_for_messages.m_parent_id).count #(SELECT COUNT(*) FROM #{JournalsForMessage.table_name} WHERE m_parent_id = #{jfm_id} )
+ update_all("m_reply_count = #{count.to_i}", ["id = ?", journals_for_messages.m_parent_id])
+ end
+
+ #如果是在项目中留言则返回该项目否则返回nil - zjc
+ def project
+ if self.jour_type == 'Project'
+ Project.find(self.jour_id)
+ else
+ nil
+ end
+ end
+
+ # 更新用户分数 -by zjc
+ def be_user_score
+ #新建了留言回复
+ if self.reply_id != 0
+ #协同得分加分
+ UserScore.joint(:reply_message, self.user,User.find(self.reply_id),self, { journals_for_messages_id: self.id })
+ update_replay_for_message(self.user,1)
+ if self.jour_type == "Project"
+ update_replay_for_message(self.user,2,self.jour)
+ end
+ end
+ end
+ # 更新用户分数 -by zjc
+ def down_user_score
+ #删除了留言回复
+ if self.reply_id != 0
+ #协同得分减分
+ UserScore.joint(:reply_message_delete, self.user,User.find(self.reply_id), { journals_for_messages_id: self.id })
+ update_replay_for_message(self.user,1)
+ if self.jour_type == "Project"
+ update_replay_for_message(self.user,2,self.jour)
+ end
+ end
+ end
+
+ # Time 2015-04-01 14:15:06
+ # Author lizanle
+ # Description 删除对应课程留言的图片资源
+ def delete_kindeditor_assets
+ delete_kindeditor_assets_from_disk self.id,7
+ end
+end
diff --git a/app/models/journals_for_message_observer.rb b/app/models/journals_for_message_observer.rb
index ee8e28b86..0db2e0043 100644
--- a/app/models/journals_for_message_observer.rb
+++ b/app/models/journals_for_message_observer.rb
@@ -1,7 +1,7 @@
-# Added by young
-class JournalsForMessageObserver < ActiveRecord::Observer
- def after_create(journals_for_message)
- Mailer.run.journals_for_message_add(User.current, journals_for_message)
- end
-end
-
+# Added by young
+class JournalsForMessageObserver < ActiveRecord::Observer
+ def after_create(journals_for_message)
+ Mailer.run.journals_for_message_add(User.current, journals_for_message)
+ end
+end
+
diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb
index da1af66a8..bf268ad66 100644
--- a/app/models/mail_handler.rb
+++ b/app/models/mail_handler.rb
@@ -1,490 +1,490 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class MailHandler < ActionMailer::Base
- include ActionView::Helpers::SanitizeHelper
- include Redmine::I18n
-
- class UnauthorizedAction < StandardError; end
- class MissingInformation < StandardError; end
-
- attr_reader :email, :user
-
- def self.receive(email, options={})
- @@handler_options = options.dup
-
- @@handler_options[:issue] ||= {}
-
- if @@handler_options[:allow_override].is_a?(String)
- @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
- end
- @@handler_options[:allow_override] ||= []
- # Project needs to be overridable if not specified
- @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
- # Status overridable by default
- @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
-
- @@handler_options[:no_account_notice] = (@@handler_options[:no_account_notice].to_s == '1')
- @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
- @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
-
- email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
- super(email)
- end
-
- def logger
- Rails.logger
- end
-
- cattr_accessor :ignored_emails_headers
- @@ignored_emails_headers = {
- 'X-Auto-Response-Suppress' => 'oof',
- 'Auto-Submitted' => /^auto-/
- }
-
- # Processes incoming emails
- # Returns the created object (eg. an issue, a message) or false
- def receive(email)
- @email = email
- sender_email = email.from.to_a.first.to_s.strip
- # Ignore emails received from the application emission address to avoid hell cycles
- if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
- if logger && logger.info
- logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
- end
- return false
- end
- # Ignore auto generated emails
- self.class.ignored_emails_headers.each do |key, ignored_value|
- value = email.header[key]
- if value
- value = value.to_s.downcase
- if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
- if logger && logger.info
- logger.info "MailHandler: ignoring email with #{key}:#{value} header"
- end
- return false
- end
- end
- end
- @user = User.find_by_mail(sender_email) if sender_email.present?
- if @user && !@user.active?
- if logger && logger.info
- logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
- end
- return false
- end
- if @user.nil?
- # Email was submitted by an unknown user
- case @@handler_options[:unknown_user]
- when 'accept'
- @user = User.anonymous
- when 'create'
- @user = create_user_from_email
- if @user
- if logger && logger.info
- logger.info "MailHandler: [#{@user.login}] account created"
- end
- add_user_to_group(@@handler_options[:default_group])
- unless @@handler_options[:no_account_notice]
- Mailer.run.account_information(@user, @user.password)
- end
- else
- if logger && logger.error
- logger.error "MailHandler: could not create account for [#{sender_email}]"
- end
- return false
- end
- else
- # Default behaviour, emails from unknown users are ignored
- if logger && logger.info
- logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
- end
- return false
- end
- end
- User.current = @user
- dispatch
- end
-
- private
-
- MESSAGE_ID_RE = %r{^ e
- # TODO: send a email to the user
- logger.error e.message if logger
- false
- rescue MissingInformation => e
- logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
- false
- rescue UnauthorizedAction => e
- logger.error "MailHandler: unauthorized attempt from #{user}" if logger
- false
- end
-
- def dispatch_to_default
- receive_issue
- end
-
- # Creates a new issue
- def receive_issue
- project = target_project
- # check permission
- unless @@handler_options[:no_permission_check]
- raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
- end
-
- issue = Issue.new(:author => user, :project => project)
- issue.safe_attributes = issue_attributes_from_keywords(issue)
- issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
- issue.subject = cleaned_up_subject
- if issue.subject.blank?
- issue.subject = '(no subject)'
- end
- issue.description = cleaned_up_text_body
-
- # add To and Cc as watchers before saving so the watchers can reply to Redmine
- add_watchers(issue)
- issue.save!
- add_attachments(issue)
- logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
- issue
- end
-
- # Adds a note to an existing issue
- def receive_issue_reply(issue_id, from_journal=nil)
- issue = Issue.find_by_id(issue_id)
- return unless issue
- # check permission
- unless @@handler_options[:no_permission_check]
- unless user.allowed_to?(:add_issue_notes, issue.project) ||
- user.allowed_to?(:edit_issues, issue.project)
- raise UnauthorizedAction
- end
- end
-
- # ignore CLI-supplied defaults for new issues
- @@handler_options[:issue].clear
-
- journal = issue.init_journal(user)
- if from_journal && from_journal.private_notes?
- # If the received email was a reply to a private note, make the added note private
- issue.private_notes = true
- end
- issue.safe_attributes = issue_attributes_from_keywords(issue)
- issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
- journal.notes = cleaned_up_text_body
- add_attachments(issue)
- issue.save!
- if logger && logger.info
- logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
- end
- journal
- end
-
- # Reply will be added to the issue
- def receive_journal_reply(journal_id)
- journal = Journal.find_by_id(journal_id)
- if journal && journal.journalized_type == 'Issue'
- receive_issue_reply(journal.journalized_id, journal)
- end
- end
-
- # Receives a reply to a forum message
- def receive_message_reply(message_id)
- message = Message.find_by_id(message_id)
- if message
- message = message.root
-
- unless @@handler_options[:no_permission_check]
- raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
- end
-
- if !message.locked?
- reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
- :content => cleaned_up_text_body)
- reply.author = user
- reply.board = message.board
- message.children << reply
- add_attachments(reply)
- reply
- else
- if logger && logger.info
- logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
- end
- end
- end
- end
-
- def add_attachments(obj)
- if email.attachments && email.attachments.any?
- email.attachments.each do |attachment|
- obj.attachments << Attachment.create(:container => obj,
- :file => attachment.decoded,
- :filename => attachment.filename,
- :author => user,
- :content_type => attachment.mime_type)
- end
- end
- end
-
- # Adds To and Cc as watchers of the given object if the sender has the
- # appropriate permission
- def add_watchers(obj)
- if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
- addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
- unless addresses.empty?
- watchers = User.active.where('LOWER(mail) IN (?)', addresses).all
- watchers.each {|w| obj.add_watcher(w)}
- end
- end
- end
-
- def get_keyword(attr, options={})
- @keywords ||= {}
- if @keywords.has_key?(attr)
- @keywords[attr]
- else
- @keywords[attr] = begin
- if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
- (v = extract_keyword!(plain_text_body, attr, options[:format]))
- v
- elsif !@@handler_options[:issue][attr].blank?
- @@handler_options[:issue][attr]
- end
- end
- end
- end
-
- # Destructively extracts the value for +attr+ in +text+
- # Returns nil if no matching keyword found
- def extract_keyword!(text, attr, format=nil)
- keys = [attr.to_s.humanize]
- if attr.is_a?(Symbol)
- if user && user.language.present?
- keys << l("field_#{attr}", :default => '', :locale => user.language)
- end
- if Setting.default_language.present?
- keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
- end
- end
- keys.reject! {|k| k.blank?}
- keys.collect! {|k| Regexp.escape(k)}
- format ||= '.+'
- keyword = nil
- regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
- if m = text.match(regexp)
- keyword = m[2].strip
- text.gsub!(regexp, '')
- end
- keyword
- end
-
- def target_project
- # TODO: other ways to specify project:
- # * parse the email To field
- # * specific project (eg. Setting.mail_handler_target_project)
- target = Project.find_by_identifier(get_keyword(:project))
- raise MissingInformation.new('Unable to determine target project') if target.nil?
- target
- end
-
- # Returns a Hash of issue attributes extracted from keywords in the email body
- def issue_attributes_from_keywords(issue)
- assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
-
- attrs = {
- 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
- 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
- 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
- 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
- 'assigned_to_id' => assigned_to.try(:id),
- 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
- issue.project.shared_versions.named(k).first.try(:id),
- 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
- 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
- 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
- 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
- }.delete_if {|k, v| v.blank? }
-
- if issue.new_record? && attrs['tracker_id'].nil?
- attrs['tracker_id'] = issue.project.trackers.first.try(:id)
- end
-
- attrs
- end
-
- # Returns a Hash of issue custom field values extracted from keywords in the email body
- def custom_field_values_from_keywords(customized)
- customized.custom_field_values.inject({}) do |h, v|
- if keyword = get_keyword(v.custom_field.name, :override => true)
- h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
- end
- h
- end
- end
-
- # Returns the text/plain part of the email
- # If not found (eg. HTML-only email), returns the body with tags removed
- def plain_text_body
- return @plain_text_body unless @plain_text_body.nil?
-
- part = email.text_part || email.html_part || email
- @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset)
-
- # strip html tags and remove doctype directive
- @plain_text_body = strip_tags(@plain_text_body.strip)
- @plain_text_body.sub! %r{^$/)
- addr, name = m[2], m[1]
- end
- if addr.present?
- user = self.class.new_user_from_attributes(addr, name)
- if @@handler_options[:no_notification]
- user.mail_notification = 'none'
- end
- if user.save
- user
- else
- logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
- nil
- end
- else
- logger.error "MailHandler: failed to create User: no FROM address found" if logger
- nil
- end
- end
-
- # Adds the newly created user to default group
- def add_user_to_group(default_group)
- if default_group.present?
- default_group.split(',').each do |group_name|
- if group = Group.named(group_name).first
- group.users << @user
- elsif logger
- logger.warn "MailHandler: could not add user to [#{group_name}], group not found"
- end
- end
- end
- end
-
- # Removes the email body of text after the truncation configurations.
- def cleanup_body(body)
- delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
- unless delimiters.empty?
- regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
- body = body.gsub(regex, '')
- end
- body.strip
- end
-
- def find_assignee_from_keyword(keyword, issue)
- keyword = keyword.to_s.downcase
- assignable = issue.assignable_users
- assignee = nil
- assignee ||= assignable.detect {|a|
- a.mail.to_s.downcase == keyword ||
- a.login.to_s.downcase == keyword
- }
- if assignee.nil? && keyword.match(/ /)
- firstname, lastname = *(keyword.split) # "First Last Throwaway"
- assignee ||= assignable.detect {|a|
- a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
- a.lastname.to_s.downcase == lastname
- }
- end
- if assignee.nil?
- assignee ||= assignable.detect {|a| a.name.downcase == keyword}
- end
- assignee
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class MailHandler < ActionMailer::Base
+ include ActionView::Helpers::SanitizeHelper
+ include Redmine::I18n
+
+ class UnauthorizedAction < StandardError; end
+ class MissingInformation < StandardError; end
+
+ attr_reader :email, :user
+
+ def self.receive(email, options={})
+ @@handler_options = options.dup
+
+ @@handler_options[:issue] ||= {}
+
+ if @@handler_options[:allow_override].is_a?(String)
+ @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
+ end
+ @@handler_options[:allow_override] ||= []
+ # Project needs to be overridable if not specified
+ @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
+ # Status overridable by default
+ @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
+
+ @@handler_options[:no_account_notice] = (@@handler_options[:no_account_notice].to_s == '1')
+ @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1')
+ @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1')
+
+ email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
+ super(email)
+ end
+
+ def logger
+ Rails.logger
+ end
+
+ cattr_accessor :ignored_emails_headers
+ @@ignored_emails_headers = {
+ 'X-Auto-Response-Suppress' => 'oof',
+ 'Auto-Submitted' => /^auto-/
+ }
+
+ # Processes incoming emails
+ # Returns the created object (eg. an issue, a message) or false
+ def receive(email)
+ @email = email
+ sender_email = email.from.to_a.first.to_s.strip
+ # Ignore emails received from the application emission address to avoid hell cycles
+ if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
+ if logger && logger.info
+ logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
+ end
+ return false
+ end
+ # Ignore auto generated emails
+ self.class.ignored_emails_headers.each do |key, ignored_value|
+ value = email.header[key]
+ if value
+ value = value.to_s.downcase
+ if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
+ if logger && logger.info
+ logger.info "MailHandler: ignoring email with #{key}:#{value} header"
+ end
+ return false
+ end
+ end
+ end
+ @user = User.find_by_mail(sender_email) if sender_email.present?
+ if @user && !@user.active?
+ if logger && logger.info
+ logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
+ end
+ return false
+ end
+ if @user.nil?
+ # Email was submitted by an unknown user
+ case @@handler_options[:unknown_user]
+ when 'accept'
+ @user = User.anonymous
+ when 'create'
+ @user = create_user_from_email
+ if @user
+ if logger && logger.info
+ logger.info "MailHandler: [#{@user.login}] account created"
+ end
+ add_user_to_group(@@handler_options[:default_group])
+ unless @@handler_options[:no_account_notice]
+ Mailer.run.account_information(@user, @user.password)
+ end
+ else
+ if logger && logger.error
+ logger.error "MailHandler: could not create account for [#{sender_email}]"
+ end
+ return false
+ end
+ else
+ # Default behaviour, emails from unknown users are ignored
+ if logger && logger.info
+ logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
+ end
+ return false
+ end
+ end
+ User.current = @user
+ dispatch
+ end
+
+ private
+
+ MESSAGE_ID_RE = %r{^ e
+ # TODO: send a email to the user
+ logger.error e.message if logger
+ false
+ rescue MissingInformation => e
+ logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
+ false
+ rescue UnauthorizedAction => e
+ logger.error "MailHandler: unauthorized attempt from #{user}" if logger
+ false
+ end
+
+ def dispatch_to_default
+ receive_issue
+ end
+
+ # Creates a new issue
+ def receive_issue
+ project = target_project
+ # check permission
+ unless @@handler_options[:no_permission_check]
+ raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
+ end
+
+ issue = Issue.new(:author => user, :project => project)
+ issue.safe_attributes = issue_attributes_from_keywords(issue)
+ issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
+ issue.subject = cleaned_up_subject
+ if issue.subject.blank?
+ issue.subject = '(no subject)'
+ end
+ issue.description = cleaned_up_text_body
+
+ # add To and Cc as watchers before saving so the watchers can reply to Redmine
+ add_watchers(issue)
+ issue.save!
+ add_attachments(issue)
+ logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
+ issue
+ end
+
+ # Adds a note to an existing issue
+ def receive_issue_reply(issue_id, from_journal=nil)
+ issue = Issue.find_by_id(issue_id)
+ return unless issue
+ # check permission
+ unless @@handler_options[:no_permission_check]
+ unless user.allowed_to?(:add_issue_notes, issue.project) ||
+ user.allowed_to?(:edit_issues, issue.project)
+ raise UnauthorizedAction
+ end
+ end
+
+ # ignore CLI-supplied defaults for new issues
+ @@handler_options[:issue].clear
+
+ journal = issue.init_journal(user)
+ if from_journal && from_journal.private_notes?
+ # If the received email was a reply to a private note, make the added note private
+ issue.private_notes = true
+ end
+ issue.safe_attributes = issue_attributes_from_keywords(issue)
+ issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
+ journal.notes = cleaned_up_text_body
+ add_attachments(issue)
+ issue.save!
+ if logger && logger.info
+ logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
+ end
+ journal
+ end
+
+ # Reply will be added to the issue
+ def receive_journal_reply(journal_id)
+ journal = Journal.find_by_id(journal_id)
+ if journal && journal.journalized_type == 'Issue'
+ receive_issue_reply(journal.journalized_id, journal)
+ end
+ end
+
+ # Receives a reply to a forum message
+ def receive_message_reply(message_id)
+ message = Message.find_by_id(message_id)
+ if message
+ message = message.root
+
+ unless @@handler_options[:no_permission_check]
+ raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
+ end
+
+ if !message.locked?
+ reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
+ :content => cleaned_up_text_body)
+ reply.author = user
+ reply.board = message.board
+ message.children << reply
+ add_attachments(reply)
+ reply
+ else
+ if logger && logger.info
+ logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
+ end
+ end
+ end
+ end
+
+ def add_attachments(obj)
+ if email.attachments && email.attachments.any?
+ email.attachments.each do |attachment|
+ obj.attachments << Attachment.create(:container => obj,
+ :file => attachment.decoded,
+ :filename => attachment.filename,
+ :author => user,
+ :content_type => attachment.mime_type)
+ end
+ end
+ end
+
+ # Adds To and Cc as watchers of the given object if the sender has the
+ # appropriate permission
+ def add_watchers(obj)
+ if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
+ addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
+ unless addresses.empty?
+ watchers = User.active.where('LOWER(mail) IN (?)', addresses).all
+ watchers.each {|w| obj.add_watcher(w)}
+ end
+ end
+ end
+
+ def get_keyword(attr, options={})
+ @keywords ||= {}
+ if @keywords.has_key?(attr)
+ @keywords[attr]
+ else
+ @keywords[attr] = begin
+ if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
+ (v = extract_keyword!(plain_text_body, attr, options[:format]))
+ v
+ elsif !@@handler_options[:issue][attr].blank?
+ @@handler_options[:issue][attr]
+ end
+ end
+ end
+ end
+
+ # Destructively extracts the value for +attr+ in +text+
+ # Returns nil if no matching keyword found
+ def extract_keyword!(text, attr, format=nil)
+ keys = [attr.to_s.humanize]
+ if attr.is_a?(Symbol)
+ if user && user.language.present?
+ keys << l("field_#{attr}", :default => '', :locale => user.language)
+ end
+ if Setting.default_language.present?
+ keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
+ end
+ end
+ keys.reject! {|k| k.blank?}
+ keys.collect! {|k| Regexp.escape(k)}
+ format ||= '.+'
+ keyword = nil
+ regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
+ if m = text.match(regexp)
+ keyword = m[2].strip
+ text.gsub!(regexp, '')
+ end
+ keyword
+ end
+
+ def target_project
+ # TODO: other ways to specify project:
+ # * parse the email To field
+ # * specific project (eg. Setting.mail_handler_target_project)
+ target = Project.find_by_identifier(get_keyword(:project))
+ raise MissingInformation.new('Unable to determine target project') if target.nil?
+ target
+ end
+
+ # Returns a Hash of issue attributes extracted from keywords in the email body
+ def issue_attributes_from_keywords(issue)
+ assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
+
+ attrs = {
+ 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
+ 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
+ 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
+ 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
+ 'assigned_to_id' => assigned_to.try(:id),
+ 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
+ issue.project.shared_versions.named(k).first.try(:id),
+ 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
+ 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
+ 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
+ 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
+ }.delete_if {|k, v| v.blank? }
+
+ if issue.new_record? && attrs['tracker_id'].nil?
+ attrs['tracker_id'] = issue.project.trackers.first.try(:id)
+ end
+
+ attrs
+ end
+
+ # Returns a Hash of issue custom field values extracted from keywords in the email body
+ def custom_field_values_from_keywords(customized)
+ customized.custom_field_values.inject({}) do |h, v|
+ if keyword = get_keyword(v.custom_field.name, :override => true)
+ h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
+ end
+ h
+ end
+ end
+
+ # Returns the text/plain part of the email
+ # If not found (eg. HTML-only email), returns the body with tags removed
+ def plain_text_body
+ return @plain_text_body unless @plain_text_body.nil?
+
+ part = email.text_part || email.html_part || email
+ @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset)
+
+ # strip html tags and remove doctype directive
+ @plain_text_body = strip_tags(@plain_text_body.strip)
+ @plain_text_body.sub! %r{^$/)
+ addr, name = m[2], m[1]
+ end
+ if addr.present?
+ user = self.class.new_user_from_attributes(addr, name)
+ if @@handler_options[:no_notification]
+ user.mail_notification = 'none'
+ end
+ if user.save
+ user
+ else
+ logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
+ nil
+ end
+ else
+ logger.error "MailHandler: failed to create User: no FROM address found" if logger
+ nil
+ end
+ end
+
+ # Adds the newly created user to default group
+ def add_user_to_group(default_group)
+ if default_group.present?
+ default_group.split(',').each do |group_name|
+ if group = Group.named(group_name).first
+ group.users << @user
+ elsif logger
+ logger.warn "MailHandler: could not add user to [#{group_name}], group not found"
+ end
+ end
+ end
+ end
+
+ # Removes the email body of text after the truncation configurations.
+ def cleanup_body(body)
+ delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
+ unless delimiters.empty?
+ regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
+ body = body.gsub(regex, '')
+ end
+ body.strip
+ end
+
+ def find_assignee_from_keyword(keyword, issue)
+ keyword = keyword.to_s.downcase
+ assignable = issue.assignable_users
+ assignee = nil
+ assignee ||= assignable.detect {|a|
+ a.mail.to_s.downcase == keyword ||
+ a.login.to_s.downcase == keyword
+ }
+ if assignee.nil? && keyword.match(/ /)
+ firstname, lastname = *(keyword.split) # "First Last Throwaway"
+ assignee ||= assignable.detect {|a|
+ a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
+ a.lastname.to_s.downcase == lastname
+ }
+ end
+ if assignee.nil?
+ assignee ||= assignable.detect {|a| a.name.downcase == keyword}
+ end
+ assignee
+ end
+end
diff --git a/app/models/mailer.rb b/app/models/mailer.rb
index a6acb1635..ce3924119 100644
--- a/app/models/mailer.rb
+++ b/app/models/mailer.rb
@@ -1,908 +1,908 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class Mailer < ActionMailer::Base
- layout 'mailer'
- helper :application
- helper :issues
- helper :custom_fields
-
- include Redmine::I18n
- include CoursesHelper
- def self.default_url_options
- { :host => Setting.host_name, :protocol => Setting.protocol }
- end
-
-
- class MailerProxy
- def initialize(cls)
- @target = cls
- end
- def method_missing(name, *args, &block)
- if Setting.delayjob_enabled && Object.const_defined?('Delayed')
- @target.delay.send(name, *args, &block)
- else
- @target.send(name, *args, &block).deliver
- end
- end
- end
-
- def self.run
- MailerProxy.new(self)
- end
-
- # author: alan
- # 发送邀请未注册用户加入项目邮件
- # 功能: 在加入项目的同时自动注册用户
- def send_invite_in_project(email, project, invitor)
- @email = email
- @subject = "#{invitor.name} #{l(:label_invite_project)} #{project.name} "
- @password = newpass(6)
- @project_url = url_for(:controller => 'projects', :action => 'show', :id => project.id,
- :password => @password, :login => email)
- mail :to => email, :subject => @subject
- end
-
- # author: alan
- # 根据用户选择发送个人日报或周报
- # 发送内容: 项目【缺陷,讨论区,新闻】,课程【通知,留言,新闻】, 贴吧, 个人留言
- def send_for_user_activities(user, date_to, days)
- date_from = date_to - days.days
-
- subject = "[ #{user.show_name} : #{l(:label_day_mail)}]"
- @subject = " #{user.show_name} : #{date_to} #{l(:label_day_mail)}"
-
- date_from = "#{date_from} 17:59:59"
- date_to = "#{date_to} 17:59:59"
-
- # 生成token用于直接点击登录
- @user = user
- token = Token.new(:user =>user , :action => 'autologin')
- token.save
- @token = token
-
- @user_url = url_for(my_account_url(user,:token => @token.value))
- # 查询user参加的项目及课程
- projects = user.projects
- courses = user.courses
- project_ids = projects.map{|project| project.id}.join(",")
- course_ids = courses.map {|course| course.id}.join(",")
-
- # 查询user的缺陷,包括发布的,跟踪的以及被指派的缺陷
- sql = "select DISTINCT i.* from issues i, watchers w
- where (i.assigned_to_id = #{user.id} or i.author_id = #{user.id}
- or (w.watchable_type = 'Issue' and w.watchable_id = i.id and w.user_id = #{user.id}))
- and (i.created_on between '#{date_from}' and '#{date_to}') order by i.created_on desc"
- @issues = Issue.find_by_sql(sql)
-
- # @bids 查询课程作业,包括老师发布的作业,以及user提交作业
- # @attachments查询课程课件更新
- @attachments ||= []
-
- @bids ||= [] # 老师发布的作业
-
- unless courses.first.nil?
- count = courses.count
- count = count - 1
- for i in 0..count do
- bids = courses[i].homeworks.where("bids.created_on between '#{date_from}' and '#{date_to}'").order("bids.created_on desc")
- attachments = courses[i].attachments.where("attachments.created_on between '#{date_from}' and '#{date_to}'").order('attachments.created_on DESC')
- @bids += bids if bids.count > 0
- @attachments += attachments if attachments.count > 0
- end
- end
- # user 提交的作业
- @homeworks = HomeworkAttach.where("user_id=#{user.id} and (created_at between '#{date_from}' and '#{date_to}')").order("created_at desc")
-
- # 查询user在课程。项目中发布的讨论帖子
- messages = Message.find_by_sql("select DISTINCT * from messages where author_id = #{user.id} and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc")
- @course_messages ||= []
- @project_messages ||= []
- unless messages.first.nil?
- messages.each do |msg|
- if msg.project
- @project_messages << msg
- elsif msg.course
- @course_messages << msg
- end
- end
- end
- # 查询user在课程中发布的通知,项目中发的新闻
- @course_news = (course_ids && !course_ids.empty?) ? News.find_by_sql("select DISTINCT n.* from news n
- where n.course_id in (#{course_ids})
- and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc") : []
- @project_news = (project_ids && !project_ids.empty?) ? News.find_by_sql("select DISTINCT n.* from news n where n.project_id in (#{project_ids})
- and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc") : []
-
- # 查询user在课程及个人中留言
- @course_journal_messages = JournalsForMessage.find_by_sql("select DISTINCT * from journals_for_messages where
- jour_type='Course' and user_id = #{user.id}
- and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc")
- @user_journal_messages = user.journals_for_messages.where("m_parent_id IS NULL and (created_on between '#{date_from}' and '#{date_to}')").order('created_on DESC')
-
-
- # 查询user新建贴吧或发布帖子
- @forums = Forum.find_by_sql("select DISTINCT * from forums where creator_id = #{user.id} and (created_at between '#{date_from}' and '#{date_to}') order by created_at desc")
- @memos = Memo.find_by_sql("select DISTINCT m.* from memos m, forums f where (m.author_id = #{user.id} or (m.forum_id = f.id and f.creator_id = #{user.id}))
- and (m.created_at between '#{date_from}' and '#{date_to}') order by m.created_at desc")
-
-
- has_content = [@issues,@homeworks,@course_messages,@project_messages,@course_news,@project_news,
- @course_journal_messages,@user_journal_messages,@forums,@memos,@attachments,@bids].any? {|o|
- !o.empty?
- }
- binding.pry if Rails.env.development?
- #有内容才发,没有不发
- mail :to => user.mail,:subject => subject if has_content
- end
-
- # 公共讨论区发帖、回帖添加邮件发送信息
- def forum_message_added(memo)
- @memo = memo
- redmine_headers 'Memo' => memo.id
- @forum = memo.forum
- @author = memo.author
- @forum_url = url_for(:controller => 'forums', :action => 'show', :id => @forum.id)
- @issue_author_url = url_for(user_activities_url(@author))
- recipients ||= []
- #将帖子创建者邮箱地址加入数组
- recipients << @forum.creator.mail
- #回复人邮箱地址加入数组
- recipients << @author.mail
- # cc = wiki_content.page.wiki.watcher_recipients - recipients
-
- @memo_url = url_for(forum_memo_url(@forum, (@memo.parent_id.nil? ? @memo : @memo.parent_id)))
- mail :to => recipients,
- :subject => "[ #{l(:label_message_plural)} : #{memo.subject} #{l(:label_memo_create_succ)}]",
- :filter => true
- end
- # Builds a Mail::Message object used to email recipients of the added journals for message.
-
- # 留言分为直接留言,和对留言人留言的回复
- # 字段说明在JournalsForMessage.rb
- # 直接留言后 reply_id,m_parent_id 为空,相对应的at_user取值为nil
-
- def journals_for_message_add(user, journals_for_message)
- @user = journals_for_message.user # 留言人
- @mail = journals_for_message.jour if journals_for_message.at_user.nil? # 留言
- @mail = journals_for_message.at_user if journals_for_message.at_user
- @message = journals_for_message.notes
- @title = "#@user #{t(:label_leave_your_message, :locale => 'zh')}"
- @issue_author_url = url_for(user_activities_url(@user))
- @url = case journals_for_message.jour.class.to_s.to_sym # 判断留言的对象所属类型
- when :Bid
- course_for_bid_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
- when :Project
- return -1 if journals_for_message.jour.project_type == Project::ProjectType_project
- project_feedback_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
- when :Course
- course_feedback_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
- when :Contest
- show_contest_contest_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
- when :User
- user_newfeedback_user_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
- else
- Rails.logger.error "[Builds a Mail::Message ERROR] journalsForMessage's jour is unkown type, journalsForMessage.id = #{journals_for_message.id}"
- return -1
- end
-
- # 验证用户的收取邮件的方式
- recipients ||= []
- recipients1 ||= []
- recipients1 = @mail.mail
- recipients = journals_for_message.jour.author.mail
-
- # modify by nwb
- #如果是直接留言并且留言对象是课程
- if !journals_for_message.at_user && journals_for_message.jour.class.to_s.to_sym == :Course
- course = journals_for_message.jour
- @author = journals_for_message.user
- #课程的教师
- @members = course_all_member journals_for_message.jour
- #收件人邮箱
- @recipients ||= []
- @members.each do |teacher|
- @recipients << teacher.user.mail
- end
- mail :to => @recipients,
- :subject => "#{l(:label_your_course)}#{journals_for_message.jour.name}#{l(:label_have_message)} ",
- :filter => true
- elsif journals_for_message.jour.class.to_s.to_sym == :Bid
- if !journals_for_message.jour.author.notify_about? journals_for_message
- return -1
- end
-
- mail :to => recipients, :subject => @title,:filter => true
- elsif journals_for_message.jour.class.to_s.to_sym == :Contest
- if !journals_for_message.jour.author.notify_about? journals_for_message
- return -1
- end
- mail :to => recipients, :subject => @title,:filter => true
- else
- mail :to => recipients1, :subject => @title,:filter => true
- end
-
-
- end
-
- # Builds a Mail::Message object used to email recipients of the added issue.
- #
- # Example:
- # issue_add(issue) => Mail::Message object
- # Mailer.issue_add(issue).deliver => sends an email to issue recipients
- def issue_add(issue, recipients)
- issue_id = issue.project_index
- redmine_headers 'Project' => issue.project.identifier,
- 'Issue-Id' => issue_id,
- 'Issue-Author' => issue.author.login
- redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
- message_id issue
-
- @author = issue.author
- @issue = issue
- user = User.find_by_mail(recipients)
- token = Token.new(:user =>user , :action => 'autologin')
- token.save
- @token = token
- @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id, :token => @token.value)
-
- # edit
- @issue_author_url = url_for(user_activities_url(@author,:token => @token.value))
- @project_url = url_for(:controller => 'projects', :action => 'show', :id => issue.project_id, :token => @token.value)
-
- @user_url = url_for(my_account_url(user,:token => @token.value))
-
-
- subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}"
- mail :to => recipients,
- :subject => subject,
- :filter => true
- end
- # issue.attachments.each do |attach|
- # attachments["#{attach.filename}"] = File.read("#{attach.disk_filename}")
- # end
- # cc = issue.watcher_recipients - recipients
- #mail.attachments['test'] = File.read("#{RAILS.root}/files/2015/01/150114094010_libegl.dll")
-
-
-
-
- # Builds a Mail::Message object used to email recipients of the edited issue.
- #
- # Example:
- # issue_edit(journal) => Mail::Message object
- # Mailer.issue_edit(journal).deliver => sends an email to issue recipients
- def issue_edit(journal,recipients)
- issue = journal.journalized.reload
- issue_id = issue.project_index
- redmine_headers 'Project' => issue.project.identifier,
- 'Issue-Id' => issue_id.to_s,
- 'Issue-Author' => issue.author.login
- redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
- message_id journal
- references issue
- @author = journal.user
-
- user = User.find_by_mail(recipients)
-
- token = Token.new(:user =>user , :action => 'autologin')
- token.save
- @token = token
-
-
- # edit
- @issue_author_url = url_for(:controller => 'users', :action => 'show', :id => issue.author_id, :token => @token.value)
- @project_url = url_for(:controller => 'projects', :action => 'show', :id => issue.project_id, :token => @token.value)
- @user_url = url_for(my_account_url(user,:token => @token.value))
-
- @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id, :anchor => "change-#{journal.id}", :token => @token.value)
- s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] "
- s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
- s << issue.subject
- @issue = issue
- @journal = journal
- # @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
- mail :to => recipients,
- :subject => s,
- :filter => true
- end
-
- def self.deliver_mailer(to,cc, subject)
- mail :to => to,
- :cc => cc,
- :subject => subject
- end
-
- # 用户申请加入项目邮件通知
- def applied_project(applied)
- @project =applied.project
- redmine_headers 'Project' => @project,
- 'User' => applied.user
- @user = applied.user
- recipients = @project.manager_recipients
- s = l(:text_applied_project, :id => "##{@user.show_name}", :project => @project.name)
- @applied_url = url_for(:controller => 'projects', :action => 'settings', :id => @project.id,:tab=>'members')
- mail :to => recipients,
- :subject => s
- end
-
- def reminder(user, issues, days)
- set_language_if_valid user.language
- @issues = issues
- @days = days
- @issues_url = url_for(:controller => 'issues', :action => 'index',
- :set_filter => 1, :assigned_to_id => user.id,
- :sort => 'due_date:asc')
- mail :to => user.mail,
- :subject => l(:mail_subject_reminder, :count => issues.size, :days => days)
- end
-
- #缺陷到期邮件通知
- def issue_expire issue
- issue_id = issue.project_index
- redmine_headers 'Project' => issue.project.identifier,
- 'Issue-Id' => issue_id,
- 'Issue-Author' => issue.author.login
- redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
- message_id issue
- @author = issue.author
- @issue = issue
- @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id)
- recipients = issue.recipients
- s = l(:text_issue_expire,:issue => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}")
- mail :to => recipients,
- :subject => s
- #########################################################################################################
- #@issues = issues
- #s = l(:text_issue_expire,:issue => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}")
- #puts s + "////" + issue.assigned_to.mail
- #@issues_url = url_for(:controller => 'issues', :action => 'show',:id => issue.id)
- #mail :to => issue.assigned_to.mail,
- # :subject => s
- #########################################################################################################
- #issue_id = issue.project_index
- #redmine_headers 'Project' => issue.project.identifier,
- # 'Issue-Id' => issue_id,
- # 'Issue-Author' => issue.author.login
- #redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
- #message_id issue
- #@author = issue.author
- #@issue = issue
- #@issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue)
- #recipients = issue.recipients
- #cc = issue.watcher_recipients - recipients
- #mail :to => recipients,
- # :cc => cc,
- # :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}"
- ######################################################################################################
- end
-
-
- # Builds a Mail::Message object used to email users belonging to the added document's project.
- #
- # Example:
- # document_added(document) => Mail::Message object
- # Mailer.document_added(document).deliver => sends an email to the document's project recipients
- def document_added(document)
- redmine_headers 'Project' => document.project.identifier
- @author = User.current
- @document = document
- @issue_author_url = url_for(user_activities_url(@author))
- @document_url = url_for(:controller => 'documents', :action => 'show', :id => document)
- mail :to => document.recipients,
- :subject => "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}",
- :filter => true
- end
-
- # Builds a Mail::Message object used to email recipients of a project when an attachements are added.
- #
- # Example:
- # attachments_added(attachments) => Mail::Message object
- # Mailer.attachments_added(attachments).deliver => sends an email to the project's recipients
- def attachments_added(attachments)
- container = attachments.first.container
- added_to = ''
- added_to_url = ''
- @author = attachments.first.author
- @issue_author_url = url_for(user_activities_url(@author))
- case container.class.name
- when 'Project'
- added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
- added_to = "#{l(:label_project)}: #{container}"
- recipients = container.notified_users.select { |user| user.allowed_to?(:view_files, container) }.collect { |u| u.mail }
- when 'Course'
- added_to_url = url_for(:controller => 'files', :action => 'index', :course_id => container)
- added_to = "#{l(:label_course)}: #{container.name}"
- recipients = container.notified_users.select { |user| user.allowed_to?(:view_files, container) }.collect { |u| u.mail }
- when 'Version'
- added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
- added_to = "#{l(:label_version)}: #{container.name}"
- recipients = container.project.notified_users.select { |user| user.allowed_to?(:view_files, container.project) }.collect { |u| u.mail }
- when 'Document'
- added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
- added_to = "#{l(:label_document)}: #{container.title}"
- recipients = container.recipients
- end
- if container.class.name == 'Course'
- redmine_headers 'Course' => container.id
- @attachments = attachments
- @added_to = added_to
- @added_to_url = added_to_url
- mail :to => recipients,
- :subject => "[#{container.name}] #{l(:label_attachment_new)}",
- :filter => true
- elsif container.class.name == 'Project'
- redmine_headers 'Project' => container.id
- @attachments = attachments
- @added_to = added_to
- @added_to_url = added_to_url
- mail :to => recipients,
- :subject => "[#{container.name}] #{l(:label_attachment_new)}",
- :filter => true
- else
- redmine_headers 'Project' => container.project.identifier
- @attachments = attachments
- @added_to = added_to
- @added_to_url = added_to_url
- mail :to => recipients,
- :subject => "[#{container.project.name}] #{l(:label_attachment_new)}",
- :filter => true
- end
- end
-
- # Builds a Mail::Message object used to email recipients of a news' project when a news item is added.
- #
- # Example:
- # news_added(news) => Mail::Message object
- # Mailer.news_added(news).deliver => sends an email to the news' project recipients
- def news_added(news)
-
- if news.project
- redmine_headers 'Project' => news.project.identifier
- @author = news.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id news
- @news = news
- @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
- mail :to => news.recipients,
- :subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}",
- :filter => true
- elsif news.course
- redmine_headers 'Course' => news.course.id
- @author = news.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id news
- @news = news
- recipients = news.course.notified_users.select { |user| user.allowed_to?(:view_files, news.course) }.collect { |u| u.mail }
- @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
- mail :to => recipients,
- :subject => "[#{news.course.name}] #{l(:label_news)}: #{news.title}",
- :filter => true
- end
- end
-
- # Builds a Mail::Message object used to email recipients of a news' project when a news comment is added.
- #
- # Example:
- # news_comment_added(comment) => Mail::Message object
- # Mailer.news_comment_added(comment) => sends an email to the news' project recipients
- def news_comment_added(comment)
- news = comment.commented
- if news.project
- redmine_headers 'Project' => news.project.identifier
- @author = comment.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id comment
- @news = news
- @comment = comment
- @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
- mail :to => news.recipients,
- :cc => news.watcher_recipients,
- :subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}",
- :filter => true
- elsif news.course
- redmine_headers 'Course' => news.course.id
- @author = comment.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id comment
- @news = news
- @comment = comment
- @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
- recipients = news.course.notified_users.select { |user| user.allowed_to?(:view_files, news.course) }.collect { |u| u.mail }
-
- mail :to => recipients,
- :subject => "[#{news.course.name}] #{l(:label_news)}: #{news.title}",
- :filter => true
- end
- end
-
- # Builds a Mail::Message object used to email the recipients of the specified message that was posted.
- #
- # Example:
- # message_posted(message) => Mail::Message object
- # Mailer.message_posted(message).deliver => sends an email to the recipients
- def message_posted(message)
- if message.project
- redmine_headers 'Project' => message.project.identifier,
- 'Topic-Id' => (message.parent_id || message.id)
- @author = message.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id message
- references message.parent unless message.parent.nil?
- recipients = message.recipients
- cc = ((message.root.watcher_recipients + message.board.watcher_recipients).uniq - recipients)
- @message = message
- @message_url = url_for(message.event_url)
- mail :to => recipients,
- :cc => cc,
- :subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
- :filter => true
- elsif message.course
- redmine_headers 'Course' => message.course.id,
- 'Topic-Id' => (message.parent_id || message.id)
- @author = message.author
- @issue_author_url = url_for(user_activities_url(@author))
- message_id message
- references message.parent unless message.parent.nil?
- recipients = message.course.notified_users.select { |user| user.allowed_to?(:view_files, message.course) }.collect { |u| u.mail }
- cc = ((message.root.watcher_recipients + message.board.watcher_recipients).uniq - recipients)
- @message = message
- @message_url = url_for(message.event_url)
- mail :to => recipients,
- :cc => cc,
- :subject => "[#{message.board.course.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
- :filter => true
- end
- end
-
- # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was added.
- #
- # Example:
- # wiki_content_added(wiki_content) => Mail::Message object
- # Mailer.wiki_content_added(wiki_content).deliver => sends an email to the project's recipients
- def wiki_content_added(wiki_content)
- redmine_headers 'Project' => wiki_content.project.identifier,
- 'Wiki-Page-Id' => wiki_content.page.id
- @author = wiki_content.author
- message_id wiki_content
- recipients = wiki_content.recipients
- cc = wiki_content.page.wiki.watcher_recipients - recipients
- @wiki_content = wiki_content
- @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
- :project_id => wiki_content.project,
- :id => wiki_content.page.title)
- mail :to => recipients,
- :cc => cc,
- :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}",
- :filter => true
- end
-
- # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was updated.
- #
- # Example:
- # wiki_content_updated(wiki_content) => Mail::Message object
- # Mailer.wiki_content_updated(wiki_content).deliver => sends an email to the project's recipients
- def wiki_content_updated(wiki_content)
- redmine_headers 'Project' => wiki_content.project.identifier,
- 'Wiki-Page-Id' => wiki_content.page.id
- @author = wiki_content.author
- message_id wiki_content
- recipients = wiki_content.recipients
- cc = wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients
- @wiki_content = wiki_content
- @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
- :project_id => wiki_content.project,
- :id => wiki_content.page.title)
- @wiki_diff_url = url_for(:controller => 'wiki', :action => 'diff',
- :project_id => wiki_content.project, :id => wiki_content.page.title,
- :version => wiki_content.version)
- mail :to => recipients,
- :cc => cc,
- :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}",
- :filter => true
- end
-
- # Builds a Mail::Message object used to email the specified user their account information.
- #
- # Example:
- # account_information(user, password) => Mail::Message object
- # Mailer.account_information(user, password).deliver => sends account information to the user
- def account_information(user, password)
- set_language_if_valid user.language
- @user = user
- @password = password
- @login_url = url_for(:controller => 'account', :action => 'login')
- mail :to => user.mail,
- :subject => l(:mail_subject_register, Setting.app_title)
- end
-
- # Builds a Mail::Message object used to email all active administrators of an account activation request.
- #
- # Example:
- # account_activation_request(user) => Mail::Message object
- # Mailer.account_activation_request(user).deliver => sends an email to all active administrators
- def account_activation_request(user)
- # Send the email to all active administrators
- recipients = User.active.where(:admin => true).all.collect { |u| u.mail }.compact
- @user = user
- @url = url_for(:controller => 'users', :action => 'index',
- :status => User::STATUS_REGISTERED,
- :sort_key => 'created_on', :sort_order => 'desc')
- mail :to => recipients,
- :subject => l(:mail_subject_account_activation_request, Setting.app_title)
- end
-
- # Builds a Mail::Message object used to email the specified user that their account was activated by an administrator.
- #
- # Example:
- # account_activated(user) => Mail::Message object
- # Mailer.account_activated(user).deliver => sends an email to the registered user
- def account_activated(user)
- set_language_if_valid user.language
- @user = user
- @login_url = url_for(:controller => 'account', :action => 'login')
- mail :to => user.mail,
- :subject => l(:mail_subject_register, Setting.app_title)
- end
-
- def lost_password(token)
- set_language_if_valid(token.user.language)
- @token = token
- @url = url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
- mail :to => token.user.mail,
- :subject => l(:mail_subject_lost_password, Setting.app_title)
- end
-
- def register(token)
- set_language_if_valid(token.user.language)
- @token = token
- @url = url_for(:controller => 'account', :action => 'activate', :token => token.value)
- mail :to => token.user.mail,
- :subject => l(:mail_subject_register, Setting.app_title)
- end
-
- def test_email(user)
- set_language_if_valid(user.language)
- @url = url_for(:controller => 'welcome')
- mail :to => user.mail,
- :subject => 'forge test'
- end
-
- # Overrides default deliver! method to prevent from sending an email
- # with no recipient, cc or bcc
- def deliver!(mail = @mail)
- set_language_if_valid @initial_language
- return false if (recipients.nil? || recipients.empty?) &&
- (cc.nil? || cc.empty?) &&
- (bcc.nil? || bcc.empty?)
-
-
- # Log errors when raise_delivery_errors is set to false, Rails does not
- raise_errors = self.class.raise_delivery_errors
- self.class.raise_delivery_errors = true
- begin
- return super(mail)
- rescue Exception => e
- if raise_errors
- raise e
- elsif mylogger
- mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
- end
- ensure
- self.class.raise_delivery_errors = raise_errors
- end
- end
-
- # Sends reminders to issue assignees
- # Available options:
- # * :days => how many days in the future to remind about (defaults to 7)
- # * :tracker => id of tracker for filtering issues (defaults to all trackers)
- # * :project => id or identifier of project to process (defaults to all projects)
- # * :users => array of user/group ids who should be reminded
- def self.reminders(options={})
- days = options[:days] || 7
- project = options[:project] ? Project.find(options[:project]) : nil
- tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
- user_ids = options[:users]
-
- scope = Issue.open.where("#{Issue.table_name}.assigned_to_id IS NOT NULL" +
- " AND #{Project.table_name}.status = #{Project::STATUS_ACTIVE}" +
- " AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date
- )
- scope = scope.where(:assigned_to_id => user_ids) if user_ids.present?
- scope = scope.where(:project_id => project.id) if project
- scope = scope.where(:tracker_id => tracker.id) if tracker
-
- issues_by_assignee = scope.includes(:status, :assigned_to, :project, :tracker).all.group_by(&:assigned_to)
- issues_by_assignee.keys.each do |assignee|
- if assignee.is_a?(Group)
- assignee.users.each do |user|
- issues_by_assignee[user] ||= []
- issues_by_assignee[user] += issues_by_assignee[assignee]
- end
- end
- end
-
- issues_by_assignee.each do |assignee, issues|
- reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active?
- end
- end
-
- # Activates/desactivates email deliveries during +block+
- def self.with_deliveries(enabled = true, &block)
- was_enabled = ActionMailer::Base.perform_deliveries
- ActionMailer::Base.perform_deliveries = !!enabled
- yield
- ensure
- ActionMailer::Base.perform_deliveries = was_enabled
- end
-
- # Sends emails synchronously in the given block
- def self.with_synched_deliveries(&block)
- saved_method = ActionMailer::Base.delivery_method
- if m = saved_method.to_s.match(%r{^async_(.+)$})
- synched_method = m[1]
- ActionMailer::Base.delivery_method = synched_method.to_sym
- ActionMailer::Base.send "#{synched_method}_settings=", ActionMailer::Base.send("async_#{synched_method}_settings")
- end
- yield
- ensure
- ActionMailer::Base.delivery_method = saved_method
- end
-
- #过滤掉不是不合规则的收件人
- def filter(reps)
- r_reps = []
- if reps.is_a? Array
- reps.each do |r|
- u = User.find_by_mail(r)
- if u && u.mail_notification == 'all'
- r_reps << r
- end
- end
- elsif reps.is_a? String
- u = User.find_by_mail(reps)
- if u && u.mail_notification == 'all'
- r_reps << reps
- end
- end
- r_reps
- end
-
- def mail(headers={})
- headers.merge! 'X-Mailer' => 'Redmine',
- 'X-Redmine-Host' => Setting.host_name,
- 'X-Redmine-Site' => Setting.app_title,
- 'X-Auto-Response-Suppress' => 'OOF',
- 'Auto-Submitted' => 'auto-generated',
- 'From' => Setting.mail_from,
- 'List-Id' => "<#{Setting.mail_from.to_s.gsub('@', '.')}>"
-
- # Removes the author from the recipients and cc
- # if he doesn't want to receive notifications about what he does
- if @author && @author.logged? && @author.pref[:no_self_notified]
- headers[:to].delete(@author.mail) if headers[:to].is_a?(Array)
- headers[:cc].delete(@author.mail) if headers[:cc].is_a?(Array)
- end
-
- if headers[:filter]
- headers[:to] = filter(headers[:to])
- headers[:cc] = filter(headers[:cc])
- end
-
- if @author && @author.logged?
- redmine_headers 'Sender' => @author.login
- end
-
- # Blind carbon copy recipients
- if Setting.bcc_recipients?
- headers[:bcc] = [headers[:to], headers[:cc]].flatten.uniq.reject(&:blank?)
- headers[:to] = nil
- headers[:cc] = nil
- end
-
- if @message_id_object
- headers[:message_id] = "<#{self.class.message_id_for(@message_id_object)}>"
- end
- if @references_objects
- headers[:references] = @references_objects.collect {|o| "<#{self.class.message_id_for(o)}>"}.join(' ')
- end
-
- super headers do |format|
- format.text
- format.html unless Setting.plain_text_mail?
- end
-
- set_language_if_valid @initial_language
- end
-
- def initialize(*args)
- @initial_language = current_language
- set_language_if_valid Setting.default_language
- super
- end
-
- def self.deliver_mail(mail)
- return false if mail.to.blank? && mail.cc.blank? && mail.bcc.blank?
- Thread.new do
- super
- end
- end
-
- def self.method_missing(method, *args, &block)
- if m = method.to_s.match(%r{^deliver_(.+)$})
- ActiveSupport::Deprecation.warn "Mailer.deliver_#{m[1]}(*args) is deprecated. Use Mailer.#{m[1]}(*args).deliver instead."
- send(m[1], *args).deliver
- else
- super
- end
- end
-
-
-
- private
-
- # Appends a Redmine header field (name is prepended with 'X-Redmine-')
- def redmine_headers(h)
- h.each { |k,v| headers["X-Redmine-#{k}"] = v.to_s }
- end
-
- # Returns a predictable Message-Id for the given object
- def self.message_id_for(object)
- # id + timestamp should reduce the odds of a collision
- # as far as we don't send multiple emails for the same object
- timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
- hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{timestamp.strftime("%Y%m%d%H%M%S")}"
- host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
- host = "#{::Socket.gethostname}.redmine" if host.empty?
- "#{hash}@#{host}"
- end
-
- def message_id(object)
- @message_id_object = object
- end
-
- def references(object)
- @references_objects ||= []
- @references_objects << object
- end
-
- def mylogger
- Rails.logger
- end
-
- def add_attachments(obj)
- if email.attachments && email.attachments.any?
- email.attachments.each do |attachment|
- obj.attachments << Attachment.create(:container => obj,
- :file => attachment.decoded,
- :filename => attachment.filename,
- :author => user,
- :content_type => attachment.mime_type)
- end
- end
- end
-
- # author: alan
- # 功能: 生成len位随机字符串
- def newpass(len)
- chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
- newpass = ""
- 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
- return newpass
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Mailer < ActionMailer::Base
+ layout 'mailer'
+ helper :application
+ helper :issues
+ helper :custom_fields
+
+ include Redmine::I18n
+ include CoursesHelper
+ def self.default_url_options
+ { :host => Setting.host_name, :protocol => Setting.protocol }
+ end
+
+
+ class MailerProxy
+ def initialize(cls)
+ @target = cls
+ end
+ def method_missing(name, *args, &block)
+ if Setting.delayjob_enabled && Object.const_defined?('Delayed')
+ @target.delay.send(name, *args, &block)
+ else
+ @target.send(name, *args, &block).deliver
+ end
+ end
+ end
+
+ def self.run
+ MailerProxy.new(self)
+ end
+
+ # author: alan
+ # 发送邀请未注册用户加入项目邮件
+ # 功能: 在加入项目的同时自动注册用户
+ def send_invite_in_project(email, project, invitor)
+ @email = email
+ @subject = "#{invitor.name} #{l(:label_invite_project)} #{project.name} "
+ @password = newpass(6)
+ @project_url = url_for(:controller => 'projects', :action => 'show', :id => project.id,
+ :password => @password, :login => email)
+ mail :to => email, :subject => @subject
+ end
+
+ # author: alan
+ # 根据用户选择发送个人日报或周报
+ # 发送内容: 项目【缺陷,讨论区,新闻】,课程【通知,留言,新闻】, 贴吧, 个人留言
+ def send_for_user_activities(user, date_to, days)
+ date_from = date_to - days.days
+
+ subject = "[ #{user.show_name} : #{l(:label_day_mail)}]"
+ @subject = " #{user.show_name} : #{date_to} #{l(:label_day_mail)}"
+
+ date_from = "#{date_from} 17:59:59"
+ date_to = "#{date_to} 17:59:59"
+
+ # 生成token用于直接点击登录
+ @user = user
+ token = Token.new(:user =>user , :action => 'autologin')
+ token.save
+ @token = token
+
+ @user_url = url_for(my_account_url(user,:token => @token.value))
+ # 查询user参加的项目及课程
+ projects = user.projects
+ courses = user.courses
+ project_ids = projects.map{|project| project.id}.join(",")
+ course_ids = courses.map {|course| course.id}.join(",")
+
+ # 查询user的缺陷,包括发布的,跟踪的以及被指派的缺陷
+ sql = "select DISTINCT i.* from issues i, watchers w
+ where (i.assigned_to_id = #{user.id} or i.author_id = #{user.id}
+ or (w.watchable_type = 'Issue' and w.watchable_id = i.id and w.user_id = #{user.id}))
+ and (i.created_on between '#{date_from}' and '#{date_to}') order by i.created_on desc"
+ @issues = Issue.find_by_sql(sql)
+
+ # @bids 查询课程作业,包括老师发布的作业,以及user提交作业
+ # @attachments查询课程课件更新
+ @attachments ||= []
+
+ @bids ||= [] # 老师发布的作业
+
+ unless courses.first.nil?
+ count = courses.count
+ count = count - 1
+ for i in 0..count do
+ bids = courses[i].homeworks.where("bids.created_on between '#{date_from}' and '#{date_to}'").order("bids.created_on desc")
+ attachments = courses[i].attachments.where("attachments.created_on between '#{date_from}' and '#{date_to}'").order('attachments.created_on DESC')
+ @bids += bids if bids.count > 0
+ @attachments += attachments if attachments.count > 0
+ end
+ end
+ # user 提交的作业
+ @homeworks = HomeworkAttach.where("user_id=#{user.id} and (created_at between '#{date_from}' and '#{date_to}')").order("created_at desc")
+
+ # 查询user在课程。项目中发布的讨论帖子
+ messages = Message.find_by_sql("select DISTINCT * from messages where author_id = #{user.id} and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc")
+ @course_messages ||= []
+ @project_messages ||= []
+ unless messages.first.nil?
+ messages.each do |msg|
+ if msg.project
+ @project_messages << msg
+ elsif msg.course
+ @course_messages << msg
+ end
+ end
+ end
+ # 查询user在课程中发布的通知,项目中发的新闻
+ @course_news = (course_ids && !course_ids.empty?) ? News.find_by_sql("select DISTINCT n.* from news n
+ where n.course_id in (#{course_ids})
+ and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc") : []
+ @project_news = (project_ids && !project_ids.empty?) ? News.find_by_sql("select DISTINCT n.* from news n where n.project_id in (#{project_ids})
+ and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc") : []
+
+ # 查询user在课程及个人中留言
+ @course_journal_messages = JournalsForMessage.find_by_sql("select DISTINCT * from journals_for_messages where
+ jour_type='Course' and user_id = #{user.id}
+ and (created_on between '#{date_from}' and '#{date_to}') order by created_on desc")
+ @user_journal_messages = user.journals_for_messages.where("m_parent_id IS NULL and (created_on between '#{date_from}' and '#{date_to}')").order('created_on DESC')
+
+
+ # 查询user新建贴吧或发布帖子
+ @forums = Forum.find_by_sql("select DISTINCT * from forums where creator_id = #{user.id} and (created_at between '#{date_from}' and '#{date_to}') order by created_at desc")
+ @memos = Memo.find_by_sql("select DISTINCT m.* from memos m, forums f where (m.author_id = #{user.id} or (m.forum_id = f.id and f.creator_id = #{user.id}))
+ and (m.created_at between '#{date_from}' and '#{date_to}') order by m.created_at desc")
+
+
+ has_content = [@issues,@homeworks,@course_messages,@project_messages,@course_news,@project_news,
+ @course_journal_messages,@user_journal_messages,@forums,@memos,@attachments,@bids].any? {|o|
+ !o.empty?
+ }
+ binding.pry if Rails.env.development?
+ #有内容才发,没有不发
+ mail :to => user.mail,:subject => subject if has_content
+ end
+
+ # 公共讨论区发帖、回帖添加邮件发送信息
+ def forum_message_added(memo)
+ @memo = memo
+ redmine_headers 'Memo' => memo.id
+ @forum = memo.forum
+ @author = memo.author
+ @forum_url = url_for(:controller => 'forums', :action => 'show', :id => @forum.id)
+ @issue_author_url = url_for(user_activities_url(@author))
+ recipients ||= []
+ #将帖子创建者邮箱地址加入数组
+ recipients << @forum.creator.mail
+ #回复人邮箱地址加入数组
+ recipients << @author.mail
+ # cc = wiki_content.page.wiki.watcher_recipients - recipients
+
+ @memo_url = url_for(forum_memo_url(@forum, (@memo.parent_id.nil? ? @memo : @memo.parent_id)))
+ mail :to => recipients,
+ :subject => "[ #{l(:label_message_plural)} : #{memo.subject} #{l(:label_memo_create_succ)}]",
+ :filter => true
+ end
+ # Builds a Mail::Message object used to email recipients of the added journals for message.
+
+ # 留言分为直接留言,和对留言人留言的回复
+ # 字段说明在JournalsForMessage.rb
+ # 直接留言后 reply_id,m_parent_id 为空,相对应的at_user取值为nil
+
+ def journals_for_message_add(user, journals_for_message)
+ @user = journals_for_message.user # 留言人
+ @mail = journals_for_message.jour if journals_for_message.at_user.nil? # 留言
+ @mail = journals_for_message.at_user if journals_for_message.at_user
+ @message = journals_for_message.notes
+ @title = "#@user #{t(:label_leave_your_message, :locale => 'zh')}"
+ @issue_author_url = url_for(user_activities_url(@user))
+ @url = case journals_for_message.jour.class.to_s.to_sym # 判断留言的对象所属类型
+ when :Bid
+ course_for_bid_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
+ when :Project
+ return -1 if journals_for_message.jour.project_type == Project::ProjectType_project
+ project_feedback_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
+ when :Course
+ course_feedback_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
+ when :Contest
+ show_contest_contest_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
+ when :User
+ user_newfeedback_user_url(journals_for_message.jour, anchor: "word_li_#{journals_for_message.id}")
+ else
+ Rails.logger.error "[Builds a Mail::Message ERROR] journalsForMessage's jour is unkown type, journalsForMessage.id = #{journals_for_message.id}"
+ return -1
+ end
+
+ # 验证用户的收取邮件的方式
+ recipients ||= []
+ recipients1 ||= []
+ recipients1 = @mail.mail
+ recipients = journals_for_message.jour.author.mail
+
+ # modify by nwb
+ #如果是直接留言并且留言对象是课程
+ if !journals_for_message.at_user && journals_for_message.jour.class.to_s.to_sym == :Course
+ course = journals_for_message.jour
+ @author = journals_for_message.user
+ #课程的教师
+ @members = course_all_member journals_for_message.jour
+ #收件人邮箱
+ @recipients ||= []
+ @members.each do |teacher|
+ @recipients << teacher.user.mail
+ end
+ mail :to => @recipients,
+ :subject => "#{l(:label_your_course)}#{journals_for_message.jour.name}#{l(:label_have_message)} ",
+ :filter => true
+ elsif journals_for_message.jour.class.to_s.to_sym == :Bid
+ if !journals_for_message.jour.author.notify_about? journals_for_message
+ return -1
+ end
+
+ mail :to => recipients, :subject => @title,:filter => true
+ elsif journals_for_message.jour.class.to_s.to_sym == :Contest
+ if !journals_for_message.jour.author.notify_about? journals_for_message
+ return -1
+ end
+ mail :to => recipients, :subject => @title,:filter => true
+ else
+ mail :to => recipients1, :subject => @title,:filter => true
+ end
+
+
+ end
+
+ # Builds a Mail::Message object used to email recipients of the added issue.
+ #
+ # Example:
+ # issue_add(issue) => Mail::Message object
+ # Mailer.issue_add(issue).deliver => sends an email to issue recipients
+ def issue_add(issue, recipients)
+ issue_id = issue.project_index
+ redmine_headers 'Project' => issue.project.identifier,
+ 'Issue-Id' => issue_id,
+ 'Issue-Author' => issue.author.login
+ redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
+ message_id issue
+
+ @author = issue.author
+ @issue = issue
+ user = User.find_by_mail(recipients)
+ token = Token.new(:user =>user , :action => 'autologin')
+ token.save
+ @token = token
+ @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id, :token => @token.value)
+
+ # edit
+ @issue_author_url = url_for(user_activities_url(@author,:token => @token.value))
+ @project_url = url_for(:controller => 'projects', :action => 'show', :id => issue.project_id, :token => @token.value)
+
+ @user_url = url_for(my_account_url(user,:token => @token.value))
+
+
+ subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}"
+ mail :to => recipients,
+ :subject => subject,
+ :filter => true
+ end
+ # issue.attachments.each do |attach|
+ # attachments["#{attach.filename}"] = File.read("#{attach.disk_filename}")
+ # end
+ # cc = issue.watcher_recipients - recipients
+ #mail.attachments['test'] = File.read("#{RAILS.root}/files/2015/01/150114094010_libegl.dll")
+
+
+
+
+ # Builds a Mail::Message object used to email recipients of the edited issue.
+ #
+ # Example:
+ # issue_edit(journal) => Mail::Message object
+ # Mailer.issue_edit(journal).deliver => sends an email to issue recipients
+ def issue_edit(journal,recipients)
+ issue = journal.journalized.reload
+ issue_id = issue.project_index
+ redmine_headers 'Project' => issue.project.identifier,
+ 'Issue-Id' => issue_id.to_s,
+ 'Issue-Author' => issue.author.login
+ redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
+ message_id journal
+ references issue
+ @author = journal.user
+
+ user = User.find_by_mail(recipients)
+
+ token = Token.new(:user =>user , :action => 'autologin')
+ token.save
+ @token = token
+
+
+ # edit
+ @issue_author_url = url_for(:controller => 'users', :action => 'show', :id => issue.author_id, :token => @token.value)
+ @project_url = url_for(:controller => 'projects', :action => 'show', :id => issue.project_id, :token => @token.value)
+ @user_url = url_for(my_account_url(user,:token => @token.value))
+
+ @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id, :anchor => "change-#{journal.id}", :token => @token.value)
+ s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] "
+ s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
+ s << issue.subject
+ @issue = issue
+ @journal = journal
+ # @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}")
+ mail :to => recipients,
+ :subject => s,
+ :filter => true
+ end
+
+ def self.deliver_mailer(to,cc, subject)
+ mail :to => to,
+ :cc => cc,
+ :subject => subject
+ end
+
+ # 用户申请加入项目邮件通知
+ def applied_project(applied)
+ @project =applied.project
+ redmine_headers 'Project' => @project,
+ 'User' => applied.user
+ @user = applied.user
+ recipients = @project.manager_recipients
+ s = l(:text_applied_project, :id => "##{@user.show_name}", :project => @project.name)
+ @applied_url = url_for(:controller => 'projects', :action => 'settings', :id => @project.id,:tab=>'members')
+ mail :to => recipients,
+ :subject => s
+ end
+
+ def reminder(user, issues, days)
+ set_language_if_valid user.language
+ @issues = issues
+ @days = days
+ @issues_url = url_for(:controller => 'issues', :action => 'index',
+ :set_filter => 1, :assigned_to_id => user.id,
+ :sort => 'due_date:asc')
+ mail :to => user.mail,
+ :subject => l(:mail_subject_reminder, :count => issues.size, :days => days)
+ end
+
+ #缺陷到期邮件通知
+ def issue_expire issue
+ issue_id = issue.project_index
+ redmine_headers 'Project' => issue.project.identifier,
+ 'Issue-Id' => issue_id,
+ 'Issue-Author' => issue.author.login
+ redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
+ message_id issue
+ @author = issue.author
+ @issue = issue
+ @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue.id)
+ recipients = issue.recipients
+ s = l(:text_issue_expire,:issue => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}")
+ mail :to => recipients,
+ :subject => s
+ #########################################################################################################
+ #@issues = issues
+ #s = l(:text_issue_expire,:issue => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}")
+ #puts s + "////" + issue.assigned_to.mail
+ #@issues_url = url_for(:controller => 'issues', :action => 'show',:id => issue.id)
+ #mail :to => issue.assigned_to.mail,
+ # :subject => s
+ #########################################################################################################
+ #issue_id = issue.project_index
+ #redmine_headers 'Project' => issue.project.identifier,
+ # 'Issue-Id' => issue_id,
+ # 'Issue-Author' => issue.author.login
+ #redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
+ #message_id issue
+ #@author = issue.author
+ #@issue = issue
+ #@issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue)
+ #recipients = issue.recipients
+ #cc = issue.watcher_recipients - recipients
+ #mail :to => recipients,
+ # :cc => cc,
+ # :subject => "[#{issue.project.name} - #{issue.tracker.name} ##{issue_id}] (#{issue.status.name}) #{issue.subject}"
+ ######################################################################################################
+ end
+
+
+ # Builds a Mail::Message object used to email users belonging to the added document's project.
+ #
+ # Example:
+ # document_added(document) => Mail::Message object
+ # Mailer.document_added(document).deliver => sends an email to the document's project recipients
+ def document_added(document)
+ redmine_headers 'Project' => document.project.identifier
+ @author = User.current
+ @document = document
+ @issue_author_url = url_for(user_activities_url(@author))
+ @document_url = url_for(:controller => 'documents', :action => 'show', :id => document)
+ mail :to => document.recipients,
+ :subject => "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}",
+ :filter => true
+ end
+
+ # Builds a Mail::Message object used to email recipients of a project when an attachements are added.
+ #
+ # Example:
+ # attachments_added(attachments) => Mail::Message object
+ # Mailer.attachments_added(attachments).deliver => sends an email to the project's recipients
+ def attachments_added(attachments)
+ container = attachments.first.container
+ added_to = ''
+ added_to_url = ''
+ @author = attachments.first.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ case container.class.name
+ when 'Project'
+ added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container)
+ added_to = "#{l(:label_project)}: #{container}"
+ recipients = container.notified_users.select { |user| user.allowed_to?(:view_files, container) }.collect { |u| u.mail }
+ when 'Course'
+ added_to_url = url_for(:controller => 'files', :action => 'index', :course_id => container)
+ added_to = "#{l(:label_course)}: #{container.name}"
+ recipients = container.notified_users.select { |user| user.allowed_to?(:view_files, container) }.collect { |u| u.mail }
+ when 'Version'
+ added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project)
+ added_to = "#{l(:label_version)}: #{container.name}"
+ recipients = container.project.notified_users.select { |user| user.allowed_to?(:view_files, container.project) }.collect { |u| u.mail }
+ when 'Document'
+ added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
+ added_to = "#{l(:label_document)}: #{container.title}"
+ recipients = container.recipients
+ end
+ if container.class.name == 'Course'
+ redmine_headers 'Course' => container.id
+ @attachments = attachments
+ @added_to = added_to
+ @added_to_url = added_to_url
+ mail :to => recipients,
+ :subject => "[#{container.name}] #{l(:label_attachment_new)}",
+ :filter => true
+ elsif container.class.name == 'Project'
+ redmine_headers 'Project' => container.id
+ @attachments = attachments
+ @added_to = added_to
+ @added_to_url = added_to_url
+ mail :to => recipients,
+ :subject => "[#{container.name}] #{l(:label_attachment_new)}",
+ :filter => true
+ else
+ redmine_headers 'Project' => container.project.identifier
+ @attachments = attachments
+ @added_to = added_to
+ @added_to_url = added_to_url
+ mail :to => recipients,
+ :subject => "[#{container.project.name}] #{l(:label_attachment_new)}",
+ :filter => true
+ end
+ end
+
+ # Builds a Mail::Message object used to email recipients of a news' project when a news item is added.
+ #
+ # Example:
+ # news_added(news) => Mail::Message object
+ # Mailer.news_added(news).deliver => sends an email to the news' project recipients
+ def news_added(news)
+
+ if news.project
+ redmine_headers 'Project' => news.project.identifier
+ @author = news.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id news
+ @news = news
+ @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
+ mail :to => news.recipients,
+ :subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}",
+ :filter => true
+ elsif news.course
+ redmine_headers 'Course' => news.course.id
+ @author = news.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id news
+ @news = news
+ recipients = news.course.notified_users.select { |user| user.allowed_to?(:view_files, news.course) }.collect { |u| u.mail }
+ @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
+ mail :to => recipients,
+ :subject => "[#{news.course.name}] #{l(:label_news)}: #{news.title}",
+ :filter => true
+ end
+ end
+
+ # Builds a Mail::Message object used to email recipients of a news' project when a news comment is added.
+ #
+ # Example:
+ # news_comment_added(comment) => Mail::Message object
+ # Mailer.news_comment_added(comment) => sends an email to the news' project recipients
+ def news_comment_added(comment)
+ news = comment.commented
+ if news.project
+ redmine_headers 'Project' => news.project.identifier
+ @author = comment.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id comment
+ @news = news
+ @comment = comment
+ @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
+ mail :to => news.recipients,
+ :cc => news.watcher_recipients,
+ :subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}",
+ :filter => true
+ elsif news.course
+ redmine_headers 'Course' => news.course.id
+ @author = comment.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id comment
+ @news = news
+ @comment = comment
+ @news_url = url_for(:controller => 'news', :action => 'show', :id => news)
+ recipients = news.course.notified_users.select { |user| user.allowed_to?(:view_files, news.course) }.collect { |u| u.mail }
+
+ mail :to => recipients,
+ :subject => "[#{news.course.name}] #{l(:label_news)}: #{news.title}",
+ :filter => true
+ end
+ end
+
+ # Builds a Mail::Message object used to email the recipients of the specified message that was posted.
+ #
+ # Example:
+ # message_posted(message) => Mail::Message object
+ # Mailer.message_posted(message).deliver => sends an email to the recipients
+ def message_posted(message)
+ if message.project
+ redmine_headers 'Project' => message.project.identifier,
+ 'Topic-Id' => (message.parent_id || message.id)
+ @author = message.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id message
+ references message.parent unless message.parent.nil?
+ recipients = message.recipients
+ cc = ((message.root.watcher_recipients + message.board.watcher_recipients).uniq - recipients)
+ @message = message
+ @message_url = url_for(message.event_url)
+ mail :to => recipients,
+ :cc => cc,
+ :subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
+ :filter => true
+ elsif message.course
+ redmine_headers 'Course' => message.course.id,
+ 'Topic-Id' => (message.parent_id || message.id)
+ @author = message.author
+ @issue_author_url = url_for(user_activities_url(@author))
+ message_id message
+ references message.parent unless message.parent.nil?
+ recipients = message.course.notified_users.select { |user| user.allowed_to?(:view_files, message.course) }.collect { |u| u.mail }
+ cc = ((message.root.watcher_recipients + message.board.watcher_recipients).uniq - recipients)
+ @message = message
+ @message_url = url_for(message.event_url)
+ mail :to => recipients,
+ :cc => cc,
+ :subject => "[#{message.board.course.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
+ :filter => true
+ end
+ end
+
+ # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was added.
+ #
+ # Example:
+ # wiki_content_added(wiki_content) => Mail::Message object
+ # Mailer.wiki_content_added(wiki_content).deliver => sends an email to the project's recipients
+ def wiki_content_added(wiki_content)
+ redmine_headers 'Project' => wiki_content.project.identifier,
+ 'Wiki-Page-Id' => wiki_content.page.id
+ @author = wiki_content.author
+ message_id wiki_content
+ recipients = wiki_content.recipients
+ cc = wiki_content.page.wiki.watcher_recipients - recipients
+ @wiki_content = wiki_content
+ @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
+ :project_id => wiki_content.project,
+ :id => wiki_content.page.title)
+ mail :to => recipients,
+ :cc => cc,
+ :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_added, :id => wiki_content.page.pretty_title)}",
+ :filter => true
+ end
+
+ # Builds a Mail::Message object used to email the recipients of a project of the specified wiki content was updated.
+ #
+ # Example:
+ # wiki_content_updated(wiki_content) => Mail::Message object
+ # Mailer.wiki_content_updated(wiki_content).deliver => sends an email to the project's recipients
+ def wiki_content_updated(wiki_content)
+ redmine_headers 'Project' => wiki_content.project.identifier,
+ 'Wiki-Page-Id' => wiki_content.page.id
+ @author = wiki_content.author
+ message_id wiki_content
+ recipients = wiki_content.recipients
+ cc = wiki_content.page.wiki.watcher_recipients + wiki_content.page.watcher_recipients - recipients
+ @wiki_content = wiki_content
+ @wiki_content_url = url_for(:controller => 'wiki', :action => 'show',
+ :project_id => wiki_content.project,
+ :id => wiki_content.page.title)
+ @wiki_diff_url = url_for(:controller => 'wiki', :action => 'diff',
+ :project_id => wiki_content.project, :id => wiki_content.page.title,
+ :version => wiki_content.version)
+ mail :to => recipients,
+ :cc => cc,
+ :subject => "[#{wiki_content.project.name}] #{l(:mail_subject_wiki_content_updated, :id => wiki_content.page.pretty_title)}",
+ :filter => true
+ end
+
+ # Builds a Mail::Message object used to email the specified user their account information.
+ #
+ # Example:
+ # account_information(user, password) => Mail::Message object
+ # Mailer.account_information(user, password).deliver => sends account information to the user
+ def account_information(user, password)
+ set_language_if_valid user.language
+ @user = user
+ @password = password
+ @login_url = url_for(:controller => 'account', :action => 'login')
+ mail :to => user.mail,
+ :subject => l(:mail_subject_register, Setting.app_title)
+ end
+
+ # Builds a Mail::Message object used to email all active administrators of an account activation request.
+ #
+ # Example:
+ # account_activation_request(user) => Mail::Message object
+ # Mailer.account_activation_request(user).deliver => sends an email to all active administrators
+ def account_activation_request(user)
+ # Send the email to all active administrators
+ recipients = User.active.where(:admin => true).all.collect { |u| u.mail }.compact
+ @user = user
+ @url = url_for(:controller => 'users', :action => 'index',
+ :status => User::STATUS_REGISTERED,
+ :sort_key => 'created_on', :sort_order => 'desc')
+ mail :to => recipients,
+ :subject => l(:mail_subject_account_activation_request, Setting.app_title)
+ end
+
+ # Builds a Mail::Message object used to email the specified user that their account was activated by an administrator.
+ #
+ # Example:
+ # account_activated(user) => Mail::Message object
+ # Mailer.account_activated(user).deliver => sends an email to the registered user
+ def account_activated(user)
+ set_language_if_valid user.language
+ @user = user
+ @login_url = url_for(:controller => 'account', :action => 'login')
+ mail :to => user.mail,
+ :subject => l(:mail_subject_register, Setting.app_title)
+ end
+
+ def lost_password(token)
+ set_language_if_valid(token.user.language)
+ @token = token
+ @url = url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
+ mail :to => token.user.mail,
+ :subject => l(:mail_subject_lost_password, Setting.app_title)
+ end
+
+ def register(token)
+ set_language_if_valid(token.user.language)
+ @token = token
+ @url = url_for(:controller => 'account', :action => 'activate', :token => token.value)
+ mail :to => token.user.mail,
+ :subject => l(:mail_subject_register, Setting.app_title)
+ end
+
+ def test_email(user)
+ set_language_if_valid(user.language)
+ @url = url_for(:controller => 'welcome')
+ mail :to => user.mail,
+ :subject => 'forge test'
+ end
+
+ # Overrides default deliver! method to prevent from sending an email
+ # with no recipient, cc or bcc
+ def deliver!(mail = @mail)
+ set_language_if_valid @initial_language
+ return false if (recipients.nil? || recipients.empty?) &&
+ (cc.nil? || cc.empty?) &&
+ (bcc.nil? || bcc.empty?)
+
+
+ # Log errors when raise_delivery_errors is set to false, Rails does not
+ raise_errors = self.class.raise_delivery_errors
+ self.class.raise_delivery_errors = true
+ begin
+ return super(mail)
+ rescue Exception => e
+ if raise_errors
+ raise e
+ elsif mylogger
+ mylogger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml."
+ end
+ ensure
+ self.class.raise_delivery_errors = raise_errors
+ end
+ end
+
+ # Sends reminders to issue assignees
+ # Available options:
+ # * :days => how many days in the future to remind about (defaults to 7)
+ # * :tracker => id of tracker for filtering issues (defaults to all trackers)
+ # * :project => id or identifier of project to process (defaults to all projects)
+ # * :users => array of user/group ids who should be reminded
+ def self.reminders(options={})
+ days = options[:days] || 7
+ project = options[:project] ? Project.find(options[:project]) : nil
+ tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
+ user_ids = options[:users]
+
+ scope = Issue.open.where("#{Issue.table_name}.assigned_to_id IS NOT NULL" +
+ " AND #{Project.table_name}.status = #{Project::STATUS_ACTIVE}" +
+ " AND #{Issue.table_name}.due_date <= ?", days.day.from_now.to_date
+ )
+ scope = scope.where(:assigned_to_id => user_ids) if user_ids.present?
+ scope = scope.where(:project_id => project.id) if project
+ scope = scope.where(:tracker_id => tracker.id) if tracker
+
+ issues_by_assignee = scope.includes(:status, :assigned_to, :project, :tracker).all.group_by(&:assigned_to)
+ issues_by_assignee.keys.each do |assignee|
+ if assignee.is_a?(Group)
+ assignee.users.each do |user|
+ issues_by_assignee[user] ||= []
+ issues_by_assignee[user] += issues_by_assignee[assignee]
+ end
+ end
+ end
+
+ issues_by_assignee.each do |assignee, issues|
+ reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active?
+ end
+ end
+
+ # Activates/desactivates email deliveries during +block+
+ def self.with_deliveries(enabled = true, &block)
+ was_enabled = ActionMailer::Base.perform_deliveries
+ ActionMailer::Base.perform_deliveries = !!enabled
+ yield
+ ensure
+ ActionMailer::Base.perform_deliveries = was_enabled
+ end
+
+ # Sends emails synchronously in the given block
+ def self.with_synched_deliveries(&block)
+ saved_method = ActionMailer::Base.delivery_method
+ if m = saved_method.to_s.match(%r{^async_(.+)$})
+ synched_method = m[1]
+ ActionMailer::Base.delivery_method = synched_method.to_sym
+ ActionMailer::Base.send "#{synched_method}_settings=", ActionMailer::Base.send("async_#{synched_method}_settings")
+ end
+ yield
+ ensure
+ ActionMailer::Base.delivery_method = saved_method
+ end
+
+ #过滤掉不是不合规则的收件人
+ def filter(reps)
+ r_reps = []
+ if reps.is_a? Array
+ reps.each do |r|
+ u = User.find_by_mail(r)
+ if u && u.mail_notification == 'all'
+ r_reps << r
+ end
+ end
+ elsif reps.is_a? String
+ u = User.find_by_mail(reps)
+ if u && u.mail_notification == 'all'
+ r_reps << reps
+ end
+ end
+ r_reps
+ end
+
+ def mail(headers={})
+ headers.merge! 'X-Mailer' => 'Redmine',
+ 'X-Redmine-Host' => Setting.host_name,
+ 'X-Redmine-Site' => Setting.app_title,
+ 'X-Auto-Response-Suppress' => 'OOF',
+ 'Auto-Submitted' => 'auto-generated',
+ 'From' => Setting.mail_from,
+ 'List-Id' => "<#{Setting.mail_from.to_s.gsub('@', '.')}>"
+
+ # Removes the author from the recipients and cc
+ # if he doesn't want to receive notifications about what he does
+ if @author && @author.logged? && @author.pref[:no_self_notified]
+ headers[:to].delete(@author.mail) if headers[:to].is_a?(Array)
+ headers[:cc].delete(@author.mail) if headers[:cc].is_a?(Array)
+ end
+
+ if headers[:filter]
+ headers[:to] = filter(headers[:to])
+ headers[:cc] = filter(headers[:cc])
+ end
+
+ if @author && @author.logged?
+ redmine_headers 'Sender' => @author.login
+ end
+
+ # Blind carbon copy recipients
+ if Setting.bcc_recipients?
+ headers[:bcc] = [headers[:to], headers[:cc]].flatten.uniq.reject(&:blank?)
+ headers[:to] = nil
+ headers[:cc] = nil
+ end
+
+ if @message_id_object
+ headers[:message_id] = "<#{self.class.message_id_for(@message_id_object)}>"
+ end
+ if @references_objects
+ headers[:references] = @references_objects.collect {|o| "<#{self.class.message_id_for(o)}>"}.join(' ')
+ end
+
+ super headers do |format|
+ format.text
+ format.html unless Setting.plain_text_mail?
+ end
+
+ set_language_if_valid @initial_language
+ end
+
+ def initialize(*args)
+ @initial_language = current_language
+ set_language_if_valid Setting.default_language
+ super
+ end
+
+ def self.deliver_mail(mail)
+ return false if mail.to.blank? && mail.cc.blank? && mail.bcc.blank?
+ Thread.new do
+ super
+ end
+ end
+
+ def self.method_missing(method, *args, &block)
+ if m = method.to_s.match(%r{^deliver_(.+)$})
+ ActiveSupport::Deprecation.warn "Mailer.deliver_#{m[1]}(*args) is deprecated. Use Mailer.#{m[1]}(*args).deliver instead."
+ send(m[1], *args).deliver
+ else
+ super
+ end
+ end
+
+
+
+ private
+
+ # Appends a Redmine header field (name is prepended with 'X-Redmine-')
+ def redmine_headers(h)
+ h.each { |k,v| headers["X-Redmine-#{k}"] = v.to_s }
+ end
+
+ # Returns a predictable Message-Id for the given object
+ def self.message_id_for(object)
+ # id + timestamp should reduce the odds of a collision
+ # as far as we don't send multiple emails for the same object
+ timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on)
+ hash = "redmine.#{object.class.name.demodulize.underscore}-#{object.id}.#{timestamp.strftime("%Y%m%d%H%M%S")}"
+ host = Setting.mail_from.to_s.gsub(%r{^.*@}, '')
+ host = "#{::Socket.gethostname}.redmine" if host.empty?
+ "#{hash}@#{host}"
+ end
+
+ def message_id(object)
+ @message_id_object = object
+ end
+
+ def references(object)
+ @references_objects ||= []
+ @references_objects << object
+ end
+
+ def mylogger
+ Rails.logger
+ end
+
+ def add_attachments(obj)
+ if email.attachments && email.attachments.any?
+ email.attachments.each do |attachment|
+ obj.attachments << Attachment.create(:container => obj,
+ :file => attachment.decoded,
+ :filename => attachment.filename,
+ :author => user,
+ :content_type => attachment.mime_type)
+ end
+ end
+ end
+
+ # author: alan
+ # 功能: 生成len位随机字符串
+ def newpass(len)
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ newpass = ""
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
+ return newpass
+ end
+end
diff --git a/app/models/memo.rb b/app/models/memo.rb
index 191d4f978..2831effe3 100644
--- a/app/models/memo.rb
+++ b/app/models/memo.rb
@@ -1,179 +1,179 @@
-class Memo < ActiveRecord::Base
- include Redmine::SafeAttributes
- include UserScoreHelper
- include ApplicationHelper
- belongs_to :forum
- has_many_kindeditor_assets :assets, :dependent => :destroy
- belongs_to :author, :class_name => "User", :foreign_key => 'author_id'
- validates_presence_of :author_id, :forum_id, :subject,:content
- # 若是主题帖,则内容可以是空
- #validates :content, presence: true, if: Proc.new{|o| !o.parent_id.nil? }
- validates_length_of :subject, maximum: 50
- #validates_length_of :content, maximum: 3072
- validate :cannot_reply_to_locked_topic, :on => :create
-
- acts_as_tree :counter_cache => :replies_count, :order => "#{Memo.table_name}.created_at ASC"
- acts_as_attachable
- has_many :user_score_details, :class_name => 'UserScoreDetails',:as => :score_changeable_obj
- has_many :praise_tread, as: :praise_tread_object, dependent: :destroy
- belongs_to :last_reply, :class_name => 'Memo', :foreign_key => 'last_reply_id'
- # acts_as_searchable :column => ['subject', 'content'],
- # #:include => { :forum => :p}
- # #:project_key => "#{Forum.table_name}.project_id"
- # :date_column => "#{table_name}.created_at"
- acts_as_event :title => Proc.new {|o| "#{o.forum.name}: #{o.subject}"},
- :datetime => :updated_at,
- # :datetime => :created_at,
- :description => :content,
- :author => :author,
- :type => Proc.new {|o| o.parent_id.nil? ? 'Memo' : 'Reply'},
- :url => Proc.new {|o| {:controller => 'memos', :action => 'show', :forum_id => o.forum_id}.merge(o.parent_id.nil? ? {:id => o.id} : {:id => o.parent_id, :r => o.id, :anchor => "reply-#{o.id}"})}
- acts_as_activity_provider :author_key => :author_id,
- :func => 'memos',
- :timestamp => 'created_at'
- # :find_options => {:type => 'memos'}
- # acts_as_watchable
-
- safe_attributes "author_id",
- "subject",
- "content",
- "forum_id",
- "last_memo_id",
- "lock",
- "sticky",
- "parent_id",
- "replies_count"
-
- after_create :add_author_as_watcher, :reset_counters!, :send_mail
- # after_update :update_memos_forum
- after_destroy :reset_counters!,:delete_kindeditor_assets#,:down_user_score -- 公共区发帖暂不计入得分
- # after_create :send_notification
- # after_save :plusParentAndForum
- # after_destroy :minusParentAndForum
- #before_save :be_user_score
- # scope :visible, lambda { |*args|
- # includes(:forum => ).where()
- # }
-
- def send_mail
- Mailer.run.forum_message_added(self) if Setting.notified_events.include?('forum_message_added')
- end
-
- def cannot_reply_to_locked_topic
- errors.add :base, l(:label_memo_locked) if root.locked? && self != root
- end
-
- # def update_memos_forum
- # if forum_id_changed?
- # Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id ])
- # Forum.reset_counters!(forum_id_was)
- # Forum.reset_counters!(forum_id)
- # end
- # end
-
- def reset_counters!
- if parent && parent.id
- Memo.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
- parent.update_attribute(:updated_at, Time.now)
- end
- forum.reset_counters!
- end
-
- def sticky?
- sticky == 1
- end
-
- def replies
- Memo.where("parent_id = ?", id)
- end
-
- def locked?
- self.lock
- end
-
- def editable_by? user
- # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
- user.admin? || self.author == user
- end
-
- def destroyable_by? user
- (user && self.author == user) || user.admin?
- #self.author == user || user.admin?
- end
-
- def deleted_attach_able_by? user
- (user && user.logged? && (self.author == user) ) || user.admin?
- end
-
- private
-
- def add_author_as_watcher
- Watcher.create(:watchable => self.root, :user => author)
- end
-
- def send_notification
- if Setting.notified_events.include?('message_posted')
- Mailer.run.message_posted(self)
- end
- end
-
- def plusParentAndForum
- @forum = Forum.find(self.forum_id)
- @forum.memo_count = @forum.memo_count.to_int + 1
- @forum.last_memo_id = self.id
- if self.parent_id
- @parent_memo = Memo.find_by_id(self.parent_id)
- @parent_memo.last_reply_id = self
- @parent_memo.replies_count = @parent_memo.replies_count.to_int + 1
- @parent_memo.save
- else
- @forum.topic_count = @forum.topic_count.to_int + 1
- end
- @forum.save
- end
-
- def minusParentAndForum
- @forum = Forum.find(self.forum_id)
- @forum.memo_count = @forum.memo_count.to_int - 1
- @forum.memo_count = 0 if @forum.memo_count.to_int < 0
- # @forum.last_memo_id = Memo.reorder('created_at ASC').find_all_by_forum_id(self.forum_id).last.id
- if self.parent_id
- @parent_memo = Memo.find_by_id(self.parent_id)
- # @parent_memo.last_reply_id = Memo.reorder('created_at ASC').find_all_by_parent_id(self.parent_id).last.id
- @parent_memo.replies_count = @parent_memo.replies_count.to_int - 1
- @parent_memo.replies_count = 0 if @parent_memo.replies_count.to_int < 0
- @parent_memo.save
- else
- @forum.topic_count = @forum.topic_count.to_int - 1
- @forum.topic_count = 0 if @forum.topic_count.to_int < 0
- end
- @forum.save
- end
-
- #更新用户分数 -by zjc
- def be_user_score
- #新建memo且无parent的为发帖
- if self.parent_id.nil?
- UserScore.joint(:post_message, User.current,nil,self ,{ memo_id: self.id })
- update_memo_number(User.current,1)
-
- #新建memo且有parent的为回帖
- elsif !self.parent_id.nil?
- UserScore.joint(:reply_posting, User.current,self.parent.author,self, { memo_id: self.id })
- update_replay_for_memo(User.current,1)
- end
- end
-
- #被删除时更新用户分数
- def down_user_score
- update_memo_number(User.current,1)
- update_replay_for_memo(User.current,1)
- end
-
- # Time 2015-03-26 15:20:24
- # Author lizanle
- # Description 从硬盘上删除资源
- def delete_kindeditor_assets
- delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::MEMO
- end
-end
+class Memo < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ include UserScoreHelper
+ include ApplicationHelper
+ belongs_to :forum
+ has_many_kindeditor_assets :assets, :dependent => :destroy
+ belongs_to :author, :class_name => "User", :foreign_key => 'author_id'
+ validates_presence_of :author_id, :forum_id, :subject,:content
+ # 若是主题帖,则内容可以是空
+ #validates :content, presence: true, if: Proc.new{|o| !o.parent_id.nil? }
+ validates_length_of :subject, maximum: 50
+ #validates_length_of :content, maximum: 3072
+ validate :cannot_reply_to_locked_topic, :on => :create
+
+ acts_as_tree :counter_cache => :replies_count, :order => "#{Memo.table_name}.created_at ASC"
+ acts_as_attachable
+ has_many :user_score_details, :class_name => 'UserScoreDetails',:as => :score_changeable_obj
+ has_many :praise_tread, as: :praise_tread_object, dependent: :destroy
+ belongs_to :last_reply, :class_name => 'Memo', :foreign_key => 'last_reply_id'
+ # acts_as_searchable :column => ['subject', 'content'],
+ # #:include => { :forum => :p}
+ # #:project_key => "#{Forum.table_name}.project_id"
+ # :date_column => "#{table_name}.created_at"
+ acts_as_event :title => Proc.new {|o| "#{o.forum.name}: #{o.subject}"},
+ :datetime => :updated_at,
+ # :datetime => :created_at,
+ :description => :content,
+ :author => :author,
+ :type => Proc.new {|o| o.parent_id.nil? ? 'Memo' : 'Reply'},
+ :url => Proc.new {|o| {:controller => 'memos', :action => 'show', :forum_id => o.forum_id}.merge(o.parent_id.nil? ? {:id => o.id} : {:id => o.parent_id, :r => o.id, :anchor => "reply-#{o.id}"})}
+ acts_as_activity_provider :author_key => :author_id,
+ :func => 'memos',
+ :timestamp => 'created_at'
+ # :find_options => {:type => 'memos'}
+ # acts_as_watchable
+
+ safe_attributes "author_id",
+ "subject",
+ "content",
+ "forum_id",
+ "last_memo_id",
+ "lock",
+ "sticky",
+ "parent_id",
+ "replies_count"
+
+ after_create :add_author_as_watcher, :reset_counters!, :send_mail
+ # after_update :update_memos_forum
+ after_destroy :reset_counters!,:delete_kindeditor_assets#,:down_user_score -- 公共区发帖暂不计入得分
+ # after_create :send_notification
+ # after_save :plusParentAndForum
+ # after_destroy :minusParentAndForum
+ #before_save :be_user_score
+ # scope :visible, lambda { |*args|
+ # includes(:forum => ).where()
+ # }
+
+ def send_mail
+ Mailer.run.forum_message_added(self) if Setting.notified_events.include?('forum_message_added')
+ end
+
+ def cannot_reply_to_locked_topic
+ errors.add :base, l(:label_memo_locked) if root.locked? && self != root
+ end
+
+ # def update_memos_forum
+ # if forum_id_changed?
+ # Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id ])
+ # Forum.reset_counters!(forum_id_was)
+ # Forum.reset_counters!(forum_id)
+ # end
+ # end
+
+ def reset_counters!
+ if parent && parent.id
+ Memo.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
+ parent.update_attribute(:updated_at, Time.now)
+ end
+ forum.reset_counters!
+ end
+
+ def sticky?
+ sticky == 1
+ end
+
+ def replies
+ Memo.where("parent_id = ?", id)
+ end
+
+ def locked?
+ self.lock
+ end
+
+ def editable_by? user
+ # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
+ user.admin? || self.author == user
+ end
+
+ def destroyable_by? user
+ (user && self.author == user) || user.admin?
+ #self.author == user || user.admin?
+ end
+
+ def deleted_attach_able_by? user
+ (user && user.logged? && (self.author == user) ) || user.admin?
+ end
+
+ private
+
+ def add_author_as_watcher
+ Watcher.create(:watchable => self.root, :user => author)
+ end
+
+ def send_notification
+ if Setting.notified_events.include?('message_posted')
+ Mailer.run.message_posted(self)
+ end
+ end
+
+ def plusParentAndForum
+ @forum = Forum.find(self.forum_id)
+ @forum.memo_count = @forum.memo_count.to_int + 1
+ @forum.last_memo_id = self.id
+ if self.parent_id
+ @parent_memo = Memo.find_by_id(self.parent_id)
+ @parent_memo.last_reply_id = self
+ @parent_memo.replies_count = @parent_memo.replies_count.to_int + 1
+ @parent_memo.save
+ else
+ @forum.topic_count = @forum.topic_count.to_int + 1
+ end
+ @forum.save
+ end
+
+ def minusParentAndForum
+ @forum = Forum.find(self.forum_id)
+ @forum.memo_count = @forum.memo_count.to_int - 1
+ @forum.memo_count = 0 if @forum.memo_count.to_int < 0
+ # @forum.last_memo_id = Memo.reorder('created_at ASC').find_all_by_forum_id(self.forum_id).last.id
+ if self.parent_id
+ @parent_memo = Memo.find_by_id(self.parent_id)
+ # @parent_memo.last_reply_id = Memo.reorder('created_at ASC').find_all_by_parent_id(self.parent_id).last.id
+ @parent_memo.replies_count = @parent_memo.replies_count.to_int - 1
+ @parent_memo.replies_count = 0 if @parent_memo.replies_count.to_int < 0
+ @parent_memo.save
+ else
+ @forum.topic_count = @forum.topic_count.to_int - 1
+ @forum.topic_count = 0 if @forum.topic_count.to_int < 0
+ end
+ @forum.save
+ end
+
+ #更新用户分数 -by zjc
+ def be_user_score
+ #新建memo且无parent的为发帖
+ if self.parent_id.nil?
+ UserScore.joint(:post_message, User.current,nil,self ,{ memo_id: self.id })
+ update_memo_number(User.current,1)
+
+ #新建memo且有parent的为回帖
+ elsif !self.parent_id.nil?
+ UserScore.joint(:reply_posting, User.current,self.parent.author,self, { memo_id: self.id })
+ update_replay_for_memo(User.current,1)
+ end
+ end
+
+ #被删除时更新用户分数
+ def down_user_score
+ update_memo_number(User.current,1)
+ update_replay_for_memo(User.current,1)
+ end
+
+ # Time 2015-03-26 15:20:24
+ # Author lizanle
+ # Description 从硬盘上删除资源
+ def delete_kindeditor_assets
+ delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::MEMO
+ end
+end
diff --git a/app/models/message.rb b/app/models/message.rb
index 669495b45..bbf62b5dc 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -1,221 +1,221 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class Message < ActiveRecord::Base
- include Redmine::SafeAttributes
- include UserScoreHelper
- include ApplicationHelper
- has_many_kindeditor_assets :assets, :dependent => :destroy
- belongs_to :board
- belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
- has_many :praise_tread, as: :praise_tread_object, dependent: :destroy
-
- acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
- acts_as_attachable
- belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
-
- # added by fq
- has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
- # 被ForgeActivity虚拟关联
- has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
- # end
-
- acts_as_searchable :columns => ['subject', 'content'],
- :include => {:board => :project},
- :project_key => "#{Board.table_name}.project_id",
- :date_column => "#{table_name}.created_on"
- acts_as_searchable :columns => ['subject', 'content'],
- :include => {:board => :course},
- :course_key => "#{Board.table_name}.course_id",
- :date_column => "#{table_name}.created_at"
- acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
- :description => :content,
- :datetime => :updated_on,
- # :datetime => "#{Message.table_name}.created_on",
- :group => :parent,
- :type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
- :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
- {:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
-
- acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
- :author_key => :author_id
- acts_as_activity_provider :find_options => {:include => [{:board => :course}, :author]},
- :type => 'course_messages',
- :author_key => :author_id
- acts_as_watchable
-
- validates_presence_of :board, :subject, :content
- validates_length_of :subject, :maximum => 255
- validate :cannot_reply_to_locked_topic, :on => :create
-
- after_create :add_author_as_watcher, :reset_counters!
- after_update :update_messages_board
- after_destroy :reset_counters!,:down_user_score,:delete_kindeditor_assets
-
- after_create :act_as_activity,:be_user_score,:act_as_forge_activity, :send_mail
- #before_save :be_user_score
-
- scope :visible, lambda {|*args|
- includes(:board => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
- }
-
- scope :course_visible, lambda {|*args|
- includes(:board => :course).where(Course.allowed_to_condition(args.shift || User.current, :view_course_messages, *args))
- }
-
-
- safe_attributes 'subject', 'content'
- safe_attributes 'board_id','locked', 'sticky',
- :if => lambda {|message, user|
- if message.project
- user.allowed_to?(:edit_messages, message.project)
- else
- user.allowed_to?(:edit_messages, message.course)
- end
- }
-
- def visible?(user=User.current)
- if project
- !user.nil? && user.allowed_to?(:view_messages, project)
- elsif course
- !user.nil? && user.allowed_to?(:view_messages, course)
- end
- end
-
- def cannot_reply_to_locked_topic
- # Can not reply to a locked topic
- errors.add :base, 'Topic is locked' if root.locked? && self != root
- end
-
- def update_messages_board
- if board_id_changed?
- Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id])
- Board.reset_counters!(board_id_was)
- Board.reset_counters!(board_id)
- end
- end
-
- def reset_counters!
- if parent && parent.id
- Message.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
- end
- board.reset_counters!
- end
-
- def sticky=(arg)
- write_attribute :sticky, (arg == true || arg.to_s == '1' ? 1 : 0)
- end
-
- def sticky?
- sticky == 1
- end
-
- def project
- board.project
- end
-
- def course
- board.course
- end
-
- def course_editable_by?(usr)
- usr && usr.logged? && (usr.allowed_to?(:edit_messages, course) || (self.author == usr && usr.allowed_to?(:edit_own_messages, course)))
- end
-
- def course_destroyable_by?(usr)
- usr && usr.logged? && (usr.allowed_to?(:delete_messages, course) || (self.author == usr && usr.allowed_to?(:delete_own_messages, course)))
- end
-
- def editable_by?(usr)
- usr && usr.logged? && (usr.allowed_to?(:edit_messages, project) || (self.author == usr && usr.allowed_to?(:edit_own_messages, project)))
- end
-
- def destroyable_by?(usr)
- usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project)))
- end
-
- private
-
- def add_author_as_watcher
- Watcher.create(:watchable => self.root, :user => author)
- end
-
- # fq
- def act_as_activity
- self.acts << Activity.new(:user_id => self.author_id)
- end
- # end
-
- # Time 2015-02-27 14:32:25
- # Author lizanle
- # Description
- def act_as_forge_activity
- # 如果project为空,那么就是课程相关的消息
- if !self.board.project.nil?
- self.forge_acts << ForgeActivity.new(:user_id => self.author_id,
- :project_id => self.board.project.id)
- end
- end
-
- #更新用户分数 -by zjc
- def be_user_score
- #新建message且无parent的为发帖
-
- if self.parent_id.nil? && !self.board.project.nil?
- UserScore.joint(:post_message, self.author,nil,self, { message_id: self.id })
- update_memo_number(self.author,1)
- if self.board.project_id != -1
- update_memo_number(self.author,2,self.board.project)
- end
- #新建message且有parent的为回帖
- elsif !self.parent_id.nil? && !self.board.project.nil?
- UserScore.joint(:reply_posting, self.author,self.parent.author,self, { message_id: self.id })
- update_replay_for_memo(self.author,1)
- if self.board.project_id != -1
- update_replay_for_memo(self.author,2,self.board.project)
- end
- end
- end
-
- #减少用户分数
- def down_user_score
- if self.parent_id.nil? && !self.board.project.nil?
- UserScore.joint(:delete_message, self.author,nil,self, { message_id: self.id })
- update_memo_number(User.current,1)
- if self.board.project_id != -1
- update_memo_number(self.author,2,self.board.project)
- end
- elsif !self.parent_id.nil? && !self.board.project.nil?
- UserScore.joint(:reply_deleting, self.author,self.parent.author,self, { message_id: self.id })
- update_replay_for_memo(User.current,1)
- if self.board.project_id != -1
- update_replay_for_memo(self.author,2,self.board.project)
- end
- end
- end
-
- def send_mail
- Mailer.run.message_posted(self) if Setting.notified_events.include?('message_posted')
- end
-
- # Time 2015-03-31 09:15:06
- # Author lizanle
- # Description 删除对应消息的图片资源
- def delete_kindeditor_assets
- delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::MESSAGE
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class Message < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ include UserScoreHelper
+ include ApplicationHelper
+ has_many_kindeditor_assets :assets, :dependent => :destroy
+ belongs_to :board
+ belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
+ has_many :praise_tread, as: :praise_tread_object, dependent: :destroy
+
+ acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC"
+ acts_as_attachable
+ belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id'
+
+ # added by fq
+ has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
+ # 被ForgeActivity虚拟关联
+ has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
+ # end
+
+ acts_as_searchable :columns => ['subject', 'content'],
+ :include => {:board => :project},
+ :project_key => "#{Board.table_name}.project_id",
+ :date_column => "#{table_name}.created_on"
+ acts_as_searchable :columns => ['subject', 'content'],
+ :include => {:board => :course},
+ :course_key => "#{Board.table_name}.course_id",
+ :date_column => "#{table_name}.created_at"
+ acts_as_event :title => Proc.new {|o| "#{o.board.name}: #{o.subject}"},
+ :description => :content,
+ :datetime => :updated_on,
+ # :datetime => "#{Message.table_name}.created_on",
+ :group => :parent,
+ :type => Proc.new {|o| o.parent_id.nil? ? 'message' : 'reply'},
+ :url => Proc.new {|o| {:controller => 'messages', :action => 'show', :board_id => o.board_id}.merge(o.parent_id.nil? ? {:id => o.id} :
+ {:id => o.parent_id, :r => o.id, :anchor => "message-#{o.id}"})}
+
+ acts_as_activity_provider :find_options => {:include => [{:board => :project}, :author]},
+ :author_key => :author_id
+ acts_as_activity_provider :find_options => {:include => [{:board => :course}, :author]},
+ :type => 'course_messages',
+ :author_key => :author_id
+ acts_as_watchable
+
+ validates_presence_of :board, :subject, :content
+ validates_length_of :subject, :maximum => 255
+ validate :cannot_reply_to_locked_topic, :on => :create
+
+ after_create :add_author_as_watcher, :reset_counters!
+ after_update :update_messages_board
+ after_destroy :reset_counters!,:down_user_score,:delete_kindeditor_assets
+
+ after_create :act_as_activity,:be_user_score,:act_as_forge_activity, :send_mail
+ #before_save :be_user_score
+
+ scope :visible, lambda {|*args|
+ includes(:board => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_messages, *args))
+ }
+
+ scope :course_visible, lambda {|*args|
+ includes(:board => :course).where(Course.allowed_to_condition(args.shift || User.current, :view_course_messages, *args))
+ }
+
+
+ safe_attributes 'subject', 'content'
+ safe_attributes 'board_id','locked', 'sticky',
+ :if => lambda {|message, user|
+ if message.project
+ user.allowed_to?(:edit_messages, message.project)
+ else
+ user.allowed_to?(:edit_messages, message.course)
+ end
+ }
+
+ def visible?(user=User.current)
+ if project
+ !user.nil? && user.allowed_to?(:view_messages, project)
+ elsif course
+ !user.nil? && user.allowed_to?(:view_messages, course)
+ end
+ end
+
+ def cannot_reply_to_locked_topic
+ # Can not reply to a locked topic
+ errors.add :base, 'Topic is locked' if root.locked? && self != root
+ end
+
+ def update_messages_board
+ if board_id_changed?
+ Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id])
+ Board.reset_counters!(board_id_was)
+ Board.reset_counters!(board_id)
+ end
+ end
+
+ def reset_counters!
+ if parent && parent.id
+ Message.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
+ end
+ board.reset_counters!
+ end
+
+ def sticky=(arg)
+ write_attribute :sticky, (arg == true || arg.to_s == '1' ? 1 : 0)
+ end
+
+ def sticky?
+ sticky == 1
+ end
+
+ def project
+ board.project
+ end
+
+ def course
+ board.course
+ end
+
+ def course_editable_by?(usr)
+ usr && usr.logged? && (usr.allowed_to?(:edit_messages, course) || (self.author == usr && usr.allowed_to?(:edit_own_messages, course)))
+ end
+
+ def course_destroyable_by?(usr)
+ usr && usr.logged? && (usr.allowed_to?(:delete_messages, course) || (self.author == usr && usr.allowed_to?(:delete_own_messages, course)))
+ end
+
+ def editable_by?(usr)
+ usr && usr.logged? && (usr.allowed_to?(:edit_messages, project) || (self.author == usr && usr.allowed_to?(:edit_own_messages, project)))
+ end
+
+ def destroyable_by?(usr)
+ usr && usr.logged? && (usr.allowed_to?(:delete_messages, project) || (self.author == usr && usr.allowed_to?(:delete_own_messages, project)))
+ end
+
+ private
+
+ def add_author_as_watcher
+ Watcher.create(:watchable => self.root, :user => author)
+ end
+
+ # fq
+ def act_as_activity
+ self.acts << Activity.new(:user_id => self.author_id)
+ end
+ # end
+
+ # Time 2015-02-27 14:32:25
+ # Author lizanle
+ # Description
+ def act_as_forge_activity
+ # 如果project为空,那么就是课程相关的消息
+ if !self.board.project.nil?
+ self.forge_acts << ForgeActivity.new(:user_id => self.author_id,
+ :project_id => self.board.project.id)
+ end
+ end
+
+ #更新用户分数 -by zjc
+ def be_user_score
+ #新建message且无parent的为发帖
+
+ if self.parent_id.nil? && !self.board.project.nil?
+ UserScore.joint(:post_message, self.author,nil,self, { message_id: self.id })
+ update_memo_number(self.author,1)
+ if self.board.project_id != -1
+ update_memo_number(self.author,2,self.board.project)
+ end
+ #新建message且有parent的为回帖
+ elsif !self.parent_id.nil? && !self.board.project.nil?
+ UserScore.joint(:reply_posting, self.author,self.parent.author,self, { message_id: self.id })
+ update_replay_for_memo(self.author,1)
+ if self.board.project_id != -1
+ update_replay_for_memo(self.author,2,self.board.project)
+ end
+ end
+ end
+
+ #减少用户分数
+ def down_user_score
+ if self.parent_id.nil? && !self.board.project.nil?
+ UserScore.joint(:delete_message, self.author,nil,self, { message_id: self.id })
+ update_memo_number(User.current,1)
+ if self.board.project_id != -1
+ update_memo_number(self.author,2,self.board.project)
+ end
+ elsif !self.parent_id.nil? && !self.board.project.nil?
+ UserScore.joint(:reply_deleting, self.author,self.parent.author,self, { message_id: self.id })
+ update_replay_for_memo(User.current,1)
+ if self.board.project_id != -1
+ update_replay_for_memo(self.author,2,self.board.project)
+ end
+ end
+ end
+
+ def send_mail
+ Mailer.run.message_posted(self) if Setting.notified_events.include?('message_posted')
+ end
+
+ # Time 2015-03-31 09:15:06
+ # Author lizanle
+ # Description 删除对应消息的图片资源
+ def delete_kindeditor_assets
+ delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::MESSAGE
+ end
+end
diff --git a/app/models/news.rb b/app/models/news.rb
index de3ad35b0..7e809cbfd 100644
--- a/app/models/news.rb
+++ b/app/models/news.rb
@@ -1,120 +1,120 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class News < ActiveRecord::Base
- include Redmine::SafeAttributes
- belongs_to :project
- include ApplicationHelper
- has_many_kindeditor_assets :assets, :dependent => :destroy
- #added by nwb
- belongs_to :course
- belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
- has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
- # fq
- has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
- # 被ForgeActivity虚拟关联
- has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
- # end
-
- validates_presence_of :title, :description
- validates_length_of :title, :maximum => 60
- validates_length_of :summary, :maximum => 255
- validates_length_of :description, :maximum => 10000
-
- acts_as_attachable :delete_permission => :manage_news
- acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
- acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
- acts_as_activity_provider :find_options => {:include => [:project, :author]},
- :author_key => :author_id
- #added by nwb
- #课程新闻独立于项目
- acts_as_activity_provider :type => 'course_news',
- :find_options => {:include => [:course, :author]},
- :author_key => :author_id
- acts_as_watchable
-
- after_create :act_as_activity,:act_as_forge_activity,:add_author_as_watcher, :send_mail
-
- after_destroy :delete_kindeditor_assets
-
- scope :visible, lambda {|*args|
- includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
- }
-
- scope :course_visible, lambda {|*args|
- includes(:course).where(Course.allowed_to_condition(args.shift || User.current, :view_course_news, *args))
- }
- safe_attributes 'title', 'summary', 'description'
-
- def visible?(user=User.current)
- !user.nil? && user.allowed_to?(:view_news, project)
- end
-
- # Returns true if the news can be commented by user
- def commentable?(user=User.current)
- user.allowed_to?(:comment_news, project)
- end
-
- def recipients
- project.users.select {|user| user.notify_about?(self)}.map(&:mail)
- end
-
- # returns latest news for projects visible by user
- def self.latest(user = User.current, count = 5)
- visible(user).includes([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).all
- end
-
- # 新闻的短描述信息
- def short_description(length = 255)
- description.gsub(/<\/?.*?>/,"").html_safe if description
- #description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
- #description
- end
-
- private
-
- def add_author_as_watcher
- Watcher.create(:watchable => self, :user => author)
- end
- ## fq
- def act_as_activity
- self.acts << Activity.new(:user_id => self.author_id)
- end
-
- # Time 2015-02-27 15:48:17
- # Author lizanle
- # Description 公用表中也要记录
- def act_as_forge_activity
- # 如果是project为空,那么是课程相关的,不需要保存
- if !self.project.nil?
- self.forge_acts << ForgeActivity.new(:user_id => self.author_id,
- :project_id => self.project.id)
- end
- end
-
- # Time 2015-03-31 13:50:54
- # Author lizanle
- # Description 删除news后删除对应的资源
- def delete_kindeditor_assets
- delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::NEWS
- end
-
- def send_mail
- Mailer.run.news_added(self) if Setting.notified_events.include?('news_added')
- end
-
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class News < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ belongs_to :project
+ include ApplicationHelper
+ has_many_kindeditor_assets :assets, :dependent => :destroy
+ #added by nwb
+ belongs_to :course
+ belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
+ has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
+ # fq
+ has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
+ # 被ForgeActivity虚拟关联
+ has_many :forge_acts, :class_name => 'ForgeActivity',:as =>:forge_act ,:dependent => :destroy
+ # end
+
+ validates_presence_of :title, :description
+ validates_length_of :title, :maximum => 60
+ validates_length_of :summary, :maximum => 255
+ validates_length_of :description, :maximum => 10000
+
+ acts_as_attachable :delete_permission => :manage_news
+ acts_as_searchable :columns => ['title', 'summary', "#{table_name}.description"], :include => :project
+ acts_as_event :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.id}}
+ acts_as_activity_provider :find_options => {:include => [:project, :author]},
+ :author_key => :author_id
+ #added by nwb
+ #课程新闻独立于项目
+ acts_as_activity_provider :type => 'course_news',
+ :find_options => {:include => [:course, :author]},
+ :author_key => :author_id
+ acts_as_watchable
+
+ after_create :act_as_activity,:act_as_forge_activity,:add_author_as_watcher, :send_mail
+
+ after_destroy :delete_kindeditor_assets
+
+ scope :visible, lambda {|*args|
+ includes(:project).where(Project.allowed_to_condition(args.shift || User.current, :view_news, *args))
+ }
+
+ scope :course_visible, lambda {|*args|
+ includes(:course).where(Course.allowed_to_condition(args.shift || User.current, :view_course_news, *args))
+ }
+ safe_attributes 'title', 'summary', 'description'
+
+ def visible?(user=User.current)
+ !user.nil? && user.allowed_to?(:view_news, project)
+ end
+
+ # Returns true if the news can be commented by user
+ def commentable?(user=User.current)
+ user.allowed_to?(:comment_news, project)
+ end
+
+ def recipients
+ project.users.select {|user| user.notify_about?(self)}.map(&:mail)
+ end
+
+ # returns latest news for projects visible by user
+ def self.latest(user = User.current, count = 5)
+ visible(user).includes([:author, :project]).order("#{News.table_name}.created_on DESC").limit(count).all
+ end
+
+ # 新闻的短描述信息
+ def short_description(length = 255)
+ description.gsub(/<\/?.*?>/,"").html_safe if description
+ #description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+ #description
+ end
+
+ private
+
+ def add_author_as_watcher
+ Watcher.create(:watchable => self, :user => author)
+ end
+ ## fq
+ def act_as_activity
+ self.acts << Activity.new(:user_id => self.author_id)
+ end
+
+ # Time 2015-02-27 15:48:17
+ # Author lizanle
+ # Description 公用表中也要记录
+ def act_as_forge_activity
+ # 如果是project为空,那么是课程相关的,不需要保存
+ if !self.project.nil?
+ self.forge_acts << ForgeActivity.new(:user_id => self.author_id,
+ :project_id => self.project.id)
+ end
+ end
+
+ # Time 2015-03-31 13:50:54
+ # Author lizanle
+ # Description 删除news后删除对应的资源
+ def delete_kindeditor_assets
+ delete_kindeditor_assets_from_disk self.id,OwnerTypeHelper::NEWS
+ end
+
+ def send_mail
+ Mailer.run.news_added(self) if Setting.notified_events.include?('news_added')
+ end
+
+end
diff --git a/app/models/poll.rb b/app/models/poll.rb
index 943c08b51..64e9df79a 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -7,22 +7,24 @@ class Poll < ActiveRecord::Base
has_many :poll_users, :dependent => :destroy
has_many :users, :through => :poll_users #该文件被哪些用户提交答案过
# 添加课程的poll动态
-# has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
-# after_create :act_as_activity
+ has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
+ after_create :act_as_activity
-# acts_as_event :title => Proc.new {|o| "#{l(:label_my_message)} ##{o.id}: #{o.name}" },
-# :description => :description,
-# :author => :author,
-# :url => Proc.new {|o| {:controller => 'poll', :action => 'show', :id => o.id}}
+ acts_as_event :title => Proc.new {|o| "#{l(:label_course_poll)}: #{o.polls_name}" },
+ :description => :polls_description,
+ :datetime => :published_at,
+ :author => :user,
+ :url => Proc.new {|o| {:controller => 'poll', :action => 'show', :id => o.id}}
-# acts_as_activity_provider :type => 'polls',
-# :permission => :view_course_polls,
- #:find_options => {:include => [:course, :author]},
- #:timestamp => "#{self.table_name}.published_at",
-# :author_key => :author_id
+ acts_as_activity_provider :type => 'polls',
+ #:permission => :view_course_polls,
+ :find_options => {:select => "#{Poll.table_name}.*",
+ :joins => "LEFT JOIN #{Course.table_name} ON ( #{Poll.table_name}.polls_type='Course' AND #{Poll.table_name}.polls_status= 2 AND #{Poll.table_name}.polls_group_id = #{Course.table_name}.id )"},
+ :timestamp => "#{self.table_name}.published_at",
+ :author_key => :user_id
-# def act_as_activity
-# self.acts << Activity.new(:user_id => self.user_id)
-# end
+ def act_as_activity
+ self.acts << Activity.new(:user_id => self.user_id)
+ end
end
diff --git a/app/models/relative_memo.rb b/app/models/relative_memo.rb
index 181aa89f6..07767c7d6 100644
--- a/app/models/relative_memo.rb
+++ b/app/models/relative_memo.rb
@@ -1,199 +1,199 @@
-class RelativeMemo < ActiveRecord::Base
- # attr_accessible :title, :body
- include Redmine::SafeAttributes
- belongs_to :open_source_project, :class_name => "OpenSourceProject", :foreign_key => 'osp_id'
- belongs_to :author, :class_name => "User", :foreign_key => 'author_id'
-
- has_many :tags, :through => :project_tags, :class_name => 'Tag'
- has_many :project_tags, :class_name => 'ProjectTags'
-
- has_many :relation_topics, :class_name => 'RelativeMemoToOpenSourceProject'
-
- has_many :no_uses, :as => :no_use, :dependent => :delete_all
-
- has_many :bugs_to_osp, :class_name => 'BugToOsp', :foreign_key => 'relative_memo_id', :dependent => :destroy
-
-
- acts_as_taggable
-
- validates_presence_of :subject
- #validates :content, presence: true
- # validates_length_of :subject, maximum: 50
- #validates_length_of :content, maximum: 3072
- validate :cannot_reply_to_locked_topic, :on => :create
- validates_uniqueness_of :osp_id, :scope => [:subject, :content]
-
- acts_as_tree :counter_cache => :replies_count, :order => "#{RelativeMemo.table_name}.created_at ASC"
- acts_as_attachable
- belongs_to :last_reply, :class_name => 'RelativeMemo', :foreign_key => 'last_reply_id'
- # acts_as_searchable :column => ['subject', 'content'],
- # #:include => { :forum => :p}
- # #:project_key => "#{Forum.table_name}.project_id"
- # :date_column => "#{table_name}.created_at"
-
- # acts_as_event :title => Proc.new {|o| "#{o.forum.name}: #{o.subject}"},
- # :datetime => :updated_at,
- # # :datetime => :created_at,
- # :description => :content,
- # :author => :author,
- # :type => Proc.new {|o| o.parent_id.nil? ? 'Memo' : 'Reply'},
- # :url => Proc.new {|o| {:controller => 'memos', :action => 'show', :forum_id => o.forum_id}.merge(o.parent_id.nil? ? {:id => o.id} : {:id => o.parent_id, :r => o.id, :anchor => "reply-#{o.id}"})}
- # acts_as_activity_provider :author_key => :author_id,
- # :func => 'memos',
- # :timestamp => 'created_at'
-
- # :find_options => {:type => 'memos'}
- # acts_as_watchable
-
- safe_attributes "author_id",
- "subject",
- "content",
- "osp_id",
- "last_memo_id",
- "lock",
- "sticky",
- "parent_id",
- "replies_count",
- "is_quote"
-
- after_create :add_author_as_watcher, :reset_counters!
- # after_update :update_memos_forum
- after_destroy :reset_counters!
- # after_create :send_notification
- # after_save :plusParentAndForum
- # after_destroy :minusParentAndForum
-
- # scope :visible, lambda { |*args|
- # includes(:forum => ).where()
- # }
-
- def cannot_reply_to_locked_topic
- errors.add :base, l(:label_memo_locked) if root.locked? && self != root
- end
-
- def short_content(length = 25)
- str = "^(.{,#{length}})[^\n\r]*.*$"
- content.gsub(Regexp.new(str), '\1...').strip if content
- end
-
- # def update_memos_forum
- # if forum_id_changed?
- # Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id ])
- # Forum.reset_counters!(forum_id_was)
- # Forum.reset_counters!(forum_id)
- # end
- # end
-
-
- scope :no_use_for, lambda { |user_id|
- { :include => :no_uses,
- :conditions => ["#{NoUse.table_name}.user_id = ?", user_id] }
- }
-
- # 获取帖子的回复
- def replies
- memos = RelativeMemo.where("parent_id = ?", id)
- end
-
- def no_use_for?(user)
- self.no_uses.each do |no_use|
- if no_use.user_id == user.id
- return true
- end
- end
- false
- end
-
- def set_no_use(user, flag=true)
- flag ? set_filter(user) : remove_filter(user)
- end
-
- def set_filter(user)
- self.no_uses << NoUse.new(:user => user)
- end
-
- def remove_filter(user)
- return nil unless user && user.is_a?(User)
- NoUse.delete_all "no_use_type = '#{self.class}' AND no_use_id = #{self.id} AND user_id = #{user.id}"
- end
-
- def reset_counters!
- if parent && parent.id
- RelativeMemo.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
- parent.update_attribute(:updated_at, Time.now)
- end
- # forum.reset_counters!
- end
-
- def sticky?
- sticky == 1
- end
-
- def replies
- RelativeMemo.where("parent_id = ?", id)
- end
-
- def locked?
- self.lock
- end
-
- def editable_by? user
- # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
- user.admin?
- end
-
- # def destroyable_by? user
- # (user && user.logged? && (Forum.find(self.forum_id).creator_id == user.id) ) || user.admin?
- # #self.author == user || user.admin?
- # end
-
- def deleted_attach_able_by? user
- (user && user.logged? && (self.author == user) ) || user.admin?
- end
-
- private
-
- def add_author_as_watcher
- Watcher.create(:watchable => self.root, :user => author)
- end
-
- def send_notification
- if Setting.notified_events.include?('message_posted')
- Mailer.run.message_posted(self)
- end
- end
-
- # def plusParentAndForum
- # @forum = Forum.find(self.forum_id)
- # @forum.memo_count = @forum.memo_count.to_int + 1
- # @forum.last_memo_id = self.id
- # if self.parent_id
- # @parent_memo = Memo.find_by_id(self.parent_id)
- # @parent_memo.last_reply_id = self
- # @parent_memo.replies_count = @parent_memo.replies_count.to_int + 1
- # @parent_memo.save
- # else
- # @forum.topic_count = @forum.topic_count.to_int + 1
- # end
- # @forum.save
- # end
-
- # def minusParentAndForum
- # @forum = Forum.find(self.forum_id)
- # @forum.memo_count = @forum.memo_count.to_int - 1
- # @forum.memo_count = 0 if @forum.memo_count.to_int < 0
- # # @forum.last_memo_id = Memo.reorder('created_at ASC').find_all_by_forum_id(self.forum_id).last.id
- # if self.parent_id
- # @parent_memo = Memo.find_by_id(self.parent_id)
- # # @parent_memo.last_reply_id = Memo.reorder('created_at ASC').find_all_by_parent_id(self.parent_id).last.id
- # @parent_memo.replies_count = @parent_memo.replies_count.to_int - 1
- # @parent_memo.replies_count = 0 if @parent_memo.replies_count.to_int < 0
- # @parent_memo.save
- # else
- # @forum.topic_count = @forum.topic_count.to_int - 1
- # @forum.topic_count = 0 if @forum.topic_count.to_int < 0
- # end
- # @forum.save
- # end
-end
-
+class RelativeMemo < ActiveRecord::Base
+ # attr_accessible :title, :body
+ include Redmine::SafeAttributes
+ belongs_to :open_source_project, :class_name => "OpenSourceProject", :foreign_key => 'osp_id'
+ belongs_to :author, :class_name => "User", :foreign_key => 'author_id'
+
+ has_many :tags, :through => :project_tags, :class_name => 'Tag'
+ has_many :project_tags, :class_name => 'ProjectTags'
+
+ has_many :relation_topics, :class_name => 'RelativeMemoToOpenSourceProject'
+
+ has_many :no_uses, :as => :no_use, :dependent => :delete_all
+
+ has_many :bugs_to_osp, :class_name => 'BugToOsp', :foreign_key => 'relative_memo_id', :dependent => :destroy
+
+
+ acts_as_taggable
+
+ validates_presence_of :subject
+ #validates :content, presence: true
+ # validates_length_of :subject, maximum: 50
+ #validates_length_of :content, maximum: 3072
+ validate :cannot_reply_to_locked_topic, :on => :create
+ validates_uniqueness_of :osp_id, :scope => [:subject, :content]
+
+ acts_as_tree :counter_cache => :replies_count, :order => "#{RelativeMemo.table_name}.created_at ASC"
+ acts_as_attachable
+ belongs_to :last_reply, :class_name => 'RelativeMemo', :foreign_key => 'last_reply_id'
+ # acts_as_searchable :column => ['subject', 'content'],
+ # #:include => { :forum => :p}
+ # #:project_key => "#{Forum.table_name}.project_id"
+ # :date_column => "#{table_name}.created_at"
+
+ # acts_as_event :title => Proc.new {|o| "#{o.forum.name}: #{o.subject}"},
+ # :datetime => :updated_at,
+ # # :datetime => :created_at,
+ # :description => :content,
+ # :author => :author,
+ # :type => Proc.new {|o| o.parent_id.nil? ? 'Memo' : 'Reply'},
+ # :url => Proc.new {|o| {:controller => 'memos', :action => 'show', :forum_id => o.forum_id}.merge(o.parent_id.nil? ? {:id => o.id} : {:id => o.parent_id, :r => o.id, :anchor => "reply-#{o.id}"})}
+ # acts_as_activity_provider :author_key => :author_id,
+ # :func => 'memos',
+ # :timestamp => 'created_at'
+
+ # :find_options => {:type => 'memos'}
+ # acts_as_watchable
+
+ safe_attributes "author_id",
+ "subject",
+ "content",
+ "osp_id",
+ "last_memo_id",
+ "lock",
+ "sticky",
+ "parent_id",
+ "replies_count",
+ "is_quote"
+
+ after_create :add_author_as_watcher, :reset_counters!
+ # after_update :update_memos_forum
+ after_destroy :reset_counters!
+ # after_create :send_notification
+ # after_save :plusParentAndForum
+ # after_destroy :minusParentAndForum
+
+ # scope :visible, lambda { |*args|
+ # includes(:forum => ).where()
+ # }
+
+ def cannot_reply_to_locked_topic
+ errors.add :base, l(:label_memo_locked) if root.locked? && self != root
+ end
+
+ def short_content(length = 25)
+ str = "^(.{,#{length}})[^\n\r]*.*$"
+ content.gsub(Regexp.new(str), '\1...').strip if content
+ end
+
+ # def update_memos_forum
+ # if forum_id_changed?
+ # Message.update_all({:board_id => board_id}, ["id = ? OR parent_id = ?", root.id, root.id ])
+ # Forum.reset_counters!(forum_id_was)
+ # Forum.reset_counters!(forum_id)
+ # end
+ # end
+
+
+ scope :no_use_for, lambda { |user_id|
+ { :include => :no_uses,
+ :conditions => ["#{NoUse.table_name}.user_id = ?", user_id] }
+ }
+
+ # 获取帖子的回复
+ def replies
+ memos = RelativeMemo.where("parent_id = ?", id)
+ end
+
+ def no_use_for?(user)
+ self.no_uses.each do |no_use|
+ if no_use.user_id == user.id
+ return true
+ end
+ end
+ false
+ end
+
+ def set_no_use(user, flag=true)
+ flag ? set_filter(user) : remove_filter(user)
+ end
+
+ def set_filter(user)
+ self.no_uses << NoUse.new(:user => user)
+ end
+
+ def remove_filter(user)
+ return nil unless user && user.is_a?(User)
+ NoUse.delete_all "no_use_type = '#{self.class}' AND no_use_id = #{self.id} AND user_id = #{user.id}"
+ end
+
+ def reset_counters!
+ if parent && parent.id
+ RelativeMemo.update_all({:last_reply_id => parent.children.maximum(:id)}, {:id => parent.id})
+ parent.update_attribute(:updated_at, Time.now)
+ end
+ # forum.reset_counters!
+ end
+
+ def sticky?
+ sticky == 1
+ end
+
+ def replies
+ RelativeMemo.where("parent_id = ?", id)
+ end
+
+ def locked?
+ self.lock
+ end
+
+ def editable_by? user
+ # user && user.logged? || (self.author == usr && usr.allowed_to?(:edit_own_messages, project))
+ user.admin?
+ end
+
+ # def destroyable_by? user
+ # (user && user.logged? && (Forum.find(self.forum_id).creator_id == user.id) ) || user.admin?
+ # #self.author == user || user.admin?
+ # end
+
+ def deleted_attach_able_by? user
+ (user && user.logged? && (self.author == user) ) || user.admin?
+ end
+
+ private
+
+ def add_author_as_watcher
+ Watcher.create(:watchable => self.root, :user => author)
+ end
+
+ def send_notification
+ if Setting.notified_events.include?('message_posted')
+ Mailer.run.message_posted(self)
+ end
+ end
+
+ # def plusParentAndForum
+ # @forum = Forum.find(self.forum_id)
+ # @forum.memo_count = @forum.memo_count.to_int + 1
+ # @forum.last_memo_id = self.id
+ # if self.parent_id
+ # @parent_memo = Memo.find_by_id(self.parent_id)
+ # @parent_memo.last_reply_id = self
+ # @parent_memo.replies_count = @parent_memo.replies_count.to_int + 1
+ # @parent_memo.save
+ # else
+ # @forum.topic_count = @forum.topic_count.to_int + 1
+ # end
+ # @forum.save
+ # end
+
+ # def minusParentAndForum
+ # @forum = Forum.find(self.forum_id)
+ # @forum.memo_count = @forum.memo_count.to_int - 1
+ # @forum.memo_count = 0 if @forum.memo_count.to_int < 0
+ # # @forum.last_memo_id = Memo.reorder('created_at ASC').find_all_by_forum_id(self.forum_id).last.id
+ # if self.parent_id
+ # @parent_memo = Memo.find_by_id(self.parent_id)
+ # # @parent_memo.last_reply_id = Memo.reorder('created_at ASC').find_all_by_parent_id(self.parent_id).last.id
+ # @parent_memo.replies_count = @parent_memo.replies_count.to_int - 1
+ # @parent_memo.replies_count = 0 if @parent_memo.replies_count.to_int < 0
+ # @parent_memo.save
+ # else
+ # @forum.topic_count = @forum.topic_count.to_int - 1
+ # @forum.topic_count = 0 if @forum.topic_count.to_int < 0
+ # end
+ # @forum.save
+ # end
+end
+
diff --git a/app/models/user.rb b/app/models/user.rb
index 59c0ab608..7b232bf13 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,1030 +1,1030 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-require "digest/sha1"
-
-class User < Principal
- TEACHER = 0
- STUDENT = 1
- ENTERPRISE = 2
- DEVELOPER = 3
-
- include Redmine::SafeAttributes
- seems_rateable_rater
- # Different ways of displaying/sorting users
- USER_FORMATS = {
- :firstname_lastname => {
- :string => '#{firstname} #{lastname}',
- :order => %w(firstname lastname id),
- :setting_order => 1
- },
- :firstname_lastinitial => {
- :string => '#{firstname} #{lastname.to_s.chars.first}.',
- :order => %w(firstname lastname id),
- :setting_order => 2
- },
- :firstname => {
- :string => '#{firstname}',
- :order => %w(firstname id),
- :setting_order => 3
- },
- :lastname_firstname => {
- :string => '#{lastname} #{firstname}',
- :order => %w(lastname firstname id),
- :setting_order => 4
- },
- :lastname_coma_firstname => {
- :string => '#{lastname}, #{firstname}',
- :order => %w(lastname firstname id),
- :setting_order => 5
- },
- :lastname => {
- :string => '#{lastname}',
- :order => %w(lastname id),
- :setting_order => 6
- },
- :username => {
- :string => '#{login}',
- :order => %w(login id),
- :setting_order => 7
- },
- }
-
- #每日一报、一事一报、不报
- MAIL_NOTIFICATION_OPTIONS = [
- ['all', :label_user_mail_option_all],
- #['week', :label_user_mail_option_week],
- ['day', :label_user_mail_option_day],
- ['none', :label_user_mail_option_none]
- ]
-
- has_many :homework_users
- has_many :homework_attaches, :through => :homework_users
- has_many :homework_evaluations
-
- #问卷相关关关系
- has_many :poll_users, :dependent => :destroy
- has_many :poll_votes, :dependent => :destroy
- has_many :poll, :dependent => :destroy #用户创建的问卷
- has_many :answers, :source => :poll, :through => :poll_users, :dependent => :destroy #用户已经完成问答的问卷
- # end
-
- has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
- :after_remove => Proc.new {|user, group| group.user_removed(user)}
- has_many :changesets, :dependent => :nullify
- has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
- has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
- has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
- belongs_to :auth_source
- belongs_to :ucourse, :class_name => 'Course', :foreign_key => :id #huang
-## added by xianbo for delete
- has_many :biding_projects, :dependent => :destroy
- has_many :contesting_projects, :dependent => :destroy
- belongs_to :softapplication, :foreign_key => 'id', :dependent => :destroy
-##ended by xianbo
-
-#####fq
- has_many :jours, :class_name => 'JournalsForMessage', :dependent => :destroy
- has_many :journals_messages, :class_name => 'JournalsForMessage', :foreign_key => "user_id", :dependent => :destroy
- has_many :bids, :foreign_key => 'author_id', :dependent => :destroy
- has_many :contests, :foreign_key => 'author_id', :dependent => :destroy
- has_many :softapplications, :foreign_key => 'user_id', :dependent => :destroy
- has_many :journals_for_messages, :as => :jour, :dependent => :destroy
- has_many :new_jours, :as => :jour, :class_name => 'JournalsForMessage', :conditions => "status=1"
- has_many :journal_replies, :dependent => :destroy
- has_many :activities, :dependent => :destroy
- has_many :students_for_courses
- #has_many :courses, :through => :students_for_courses, :source => :project
- has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
- has_many :file_commit, :class_name => 'Attachment', :foreign_key => 'author_id', :conditions => "container_type = 'Project' or container_type = 'Version'"
-####
-# added by bai
- has_many :join_in_contests, :dependent => :destroy
- has_many :news, :foreign_key => 'author_id'
- has_many :contestnotification, :foreign_key => 'author_id'
- has_many :comments, :foreign_key => 'author_id'
- has_many :notificationcomments, :foreign_key => 'author_id'
- has_many :wiki_contents, :foreign_key => 'author_id'
- has_many :journals
- has_many :messages, :foreign_key => 'author_id'
- has_one :user_score, :dependent => :destroy
- has_many :documents # 项目中关联的文档再次与人关联
-# end
-
-######added by nie
- has_many :project_infos, :dependent => :destroy
- has_one :user_status, :dependent => :destroy
- #####
- has_many :shares ,:dependent => :destroy
-
- # add by zjc
- has_one :level, :class_name => 'UserLevels', :dependent => :destroy
- has_many :memos , :foreign_key => 'author_id'
- #####
- scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
- scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
- scope :visible, lambda {|*args|
- nil
- }
-
-
- acts_as_customizable
- ############################added by william
- acts_as_taggable
- scope :by_join_date, order("created_on DESC")
- ############################# added by liuping 关注
- acts_as_watchable
-
- has_one :user_extensions,:dependent => :destroy
- ## end
-
- # default_scope -> { includes(:user_extensions, :user_score) }
- scope :teacher, -> {
- joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::TEACHER)
- }
- scope :student, -> {
- joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::STUDENT)
- }
- scope :developer, -> {
- joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::DEVELOPER)
- }
- scope :enterprise, -> {
- joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::ENTERPRISE)
- }
-
- attr_accessor :password, :password_confirmation
- attr_accessor :last_before_login_on
- # Prevents unauthorized assignments
- attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
-
- LOGIN_LENGTH_LIMIT = 25
- MAIL_LENGTH_LIMIT = 60
-
- validates_presence_of :login, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
- validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
- validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
- # Login must contain letters, numbers, underscores only
- validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
- validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
- validates_length_of :firstname, :maximum => 30
- validates_length_of :lastname, :maximum => 30
- validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
- validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
- validates_confirmation_of :password, :allow_nil => true
- validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
- validate :validate_password_length
- # validates_email_realness_of :mail
- before_create :set_mail_notification
- before_save :update_hashed_password
- before_destroy :remove_references_before_destroy
- # added by fq
- after_create :act_as_activity
- # end
-
- scope :in_group, lambda {|group|
- group_id = group.is_a?(Group) ? group.id : group.to_i
- where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
- }
- scope :not_in_group, lambda {|group|
- group_id = group.is_a?(Group) ? group.id : group.to_i
- where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
- }
- scope :sorted, lambda { order(*User.fields_for_order_statement)}
-
- scope :like, lambda {|arg, type|
- if arg.blank?
- where(nil)
- else
- pattern = "%#{arg.to_s.strip.downcase}%"
- #where(" LOWER(concat(lastname, firstname)) LIKE :p ", :p => pattern)
- if type == "0"
- where(" LOWER(login) LIKE '#{pattern}' ")
- elsif type == "1"
- where(" LOWER(concat(lastname, firstname)) LIKE '#{pattern}' ")
- elsif type == "3"
- where(" LOWER(concat(lastname, firstname,login)) LIKE '#{pattern}' ")
- else
- where(" LOWER(mail) LIKE '#{pattern}' ")
- end
- end
- }
-
-
- # ======================================================================
-
- def extensions
- self.user_extensions ||= UserExtensions.new
- end
-
- def user_score_attr
- self.user_score ||= UserScore.new
- end
-
- # ======================================================================
-
- #选择项目成员时显示的用户信息文字
- def userInfo
- if self.realname.gsub(' ','') == "" || self.realname.nil?
- info = self.nickname;
- else
- info=self.nickname + ' (' + self.realname + ')';
- end
- info
- end
-
- ###添加留言 fq
- def add_jour(user, notes, reference_user_id = 0, options = {})
- if options.count == 0
- self.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => reference_user_id, :status => true)
- else
- jfm = self.journals_for_messages.build(options)
- jfm.save
- jfm
- end
- end
-
- # 判断用户是否加入了竞赛中 fq
- def join_in_contest?(bid)
- joined = JoinInContest.where('user_id = ? and bid_id =?', self.id, bid.id)
- if joined.size > 0
- true
- else
- false
- end
- end
-
- ### fq
- def join_in?(course)
- joined = StudentsForCourse.where('student_id = ? and course_id = ?', self.id, course.id)
- if joined.size > 0
- true
- else
- false
- end
- end
-
- def show_name
- unless self.user_extensions.nil?
- if self.user_extensions.identity == 2
- firstname
- else
- lastname+firstname
- end
- else
- lastname+firstname
- end
- end
- ## end
-
- def count_new_jour
- count = self.new_jours.count
- end
-
- #added by nie
- def count_new_journal_reply
- count = self.journal_reply.count
- end
-
- def set_mail_notification
- ##add byxianbo
- thread=Thread.new do
- self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
- true
- end
- end
-
- def update_hashed_password
- # update hashed_password if password was set
- if self.password && self.auth_source_id.blank?
- salt_password(password)
- end
- end
-
- alias :base_reload :reload
- def reload(*args)
- @name = nil
- @projects_by_role = nil
- @courses_by_role = nil
- @membership_by_project_id = nil
- base_reload(*args)
- end
-
- def mail=(arg)
- write_attribute(:mail, arg.to_s.strip)
- end
-
- def identity_url=(url)
- if url.blank?
- write_attribute(:identity_url, '')
- else
- begin
- write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
- rescue OpenIdAuthentication::InvalidOpenId
- # Invalid url, don't save
- end
- end
- self.read_attribute(:identity_url)
- end
-
- VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
- # VALID_EMAIL_REGEX = /^[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+(\.[0-9a-zA-Z_-]+)+$/
- # Returns the user that matches provided login and password, or nil
- #登录,返回用户名与密码匹配的用户
- def self.try_to_login(login, password)
- login = login.to_s.lstrip.rstrip
- password = password.to_s
-
- # Make sure no one can sign in with an empty login or password
- return nil if login.empty? || password.empty?
- if (login =~ VALID_EMAIL_REGEX)
- user = find_by_mail(login)
- else
- user = find_by_login(login)
- end
- if user
- # user is already in local database
- #return nil unless user.active?
- return nil unless user.check_password?(password)
- else
- # user is not yet registered, try to authenticate with available sources
- attrs = AuthSource.authenticate(login, password)
- if attrs
- user = new(attrs)
- user.login = login
- user.language = Setting.default_language
- if user.save
- user.reload
- logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
- end
- end
- end
- if user && !user.new_record?
- last_login_on = user.last_login_on.nil? ? '' : user.last_login_on.to_s
- user.update_column(:last_login_on, Time.now)
- end
- [user, last_login_on]
- rescue => text
- raise text
- end
-
-
- def self.try_to_autologin(key)
- user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
- if user
- user.update_column(:last_login_on, Time.now)
- user
- end
- end
-
- def self.name_formatter(formatter = nil)
- USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
- end
-
- # Returns an array of fields names than can be used to make an order statement for users
- # according to how user names are displayed
- # Examples:
- #
- # User.fields_for_order_statement => ['users.login', 'users.id']
- # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
- def self.fields_for_order_statement(table=nil)
- table ||= table_name
- name_formatter[:order].map {|field| "#{table}.#{field}"}
- end
-
- # Return user's full name for display
- def realname(formatter = nil)
- f = self.class.name_formatter(formatter)
- if formatter
- eval('"' + f[:string] + '"')
- else
- @name ||= eval('"' + f[:string] + '"')
- end
- end
-
- def nickname(formatter = nil)
- login
- end
-
- def name(formatter = nil)
- login
- end
-
- def active?
- self.status == STATUS_ACTIVE
- end
-
- def registered?
- self.status == STATUS_REGISTERED
- end
-
- def locked?
- self.status == STATUS_LOCKED
- end
-
- def activate
- self.status = STATUS_ACTIVE
- end
-
- def register
- self.status = STATUS_REGISTERED
- end
-
- def lock
- self.status = STATUS_LOCKED
- end
-
- def activate!
- update_attribute(:status, STATUS_ACTIVE)
- end
-
- def register!
- update_attribute(:status, STATUS_REGISTERED)
- end
-
- def lock!
- update_attribute(:status, STATUS_LOCKED)
- end
-
- # Returns true if +clear_password+ is the correct user's password, otherwise false
- def check_password?(clear_password)
- if auth_source_id.present?
- auth_source.authenticate(self.login, clear_password)
- else
- User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
- end
- end
- def check_password1?(clear_password)
-
- clear_password == hashed_password
-
- end
- # Generates a random salt and computes hashed_password for +clear_password+
- # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
- def salt_password(clear_password)
- self.salt = User.generate_salt
- self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
- end
-
- # Does the backend storage allow this user to change their password?
- def change_password_allowed?
- return true if auth_source.nil?
- return auth_source.allow_password_changes?
- end
-
- # Generate and set a random password. Useful for automated user creation
- # Based on Token#generate_token_value
- #
- def random_password
- chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
- password = ''
- 40.times { |i| password << chars[rand(chars.size-1)] }
- self.password = password
- self.password_confirmation = password
- self
- end
-
- def pref
- self.preference ||= UserPreference.new(:user => self)
- end
-
- def time_zone
- @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
- end
-
- def wants_comments_in_reverse_order?
- self.pref[:comments_sorting] == 'desc'
- end
-
- def wants_notificationcomments_in_reverse_order?
- self.pref[:notificationcomments_sorting] == 'desc'
- end
- # Return user's RSS key (a 40 chars long string), used to access feeds
- def rss_key
- if rss_token.nil?
- create_rss_token(:action => 'feeds')
- end
- rss_token.value
- end
-
- # Return user's API key (a 40 chars long string), used to access the API
- def api_key
- if api_token.nil?
- create_api_token(:action => 'api')
- end
- api_token.value
- end
-
- # Return an array of project ids for which the user has explicitly turned mail notifications on
- def notified_projects_ids
- @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
- end
-
- def notified_project_ids=(ids)
- Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
- Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
- @notified_projects_ids = nil
- notified_projects_ids
- end
-
- def valid_notification_options
- self.class.valid_notification_options(self)
- end
-
- # Only users that belong to more than 1 project can select projects for which they are notified
- def self.valid_notification_options(user=nil)
- # Note that @user.membership.size would fail since AR ignores
- # :include association option when doing a count
- if user.nil? || user.memberships.length < 1
- MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
- else
- MAIL_NOTIFICATION_OPTIONS
- end
- end
-
- # Find a user account by matching the exact login and then a case-insensitive
- # version. Exact matches will be given priority.
- #通过用户名查找相应的用户,若没有匹配到,则不区分大小写进行查询
- #修改:不再匹配不区分大小写情况 -zjc
- def self.find_by_login(login)
- if login.present?
- login = login.to_s
- # First look for an exact match
- user = where(:login => login).all.detect {|u| u.login == login}
- #unless user
- # # Fail over to case-insensitive if none was found
- # user = where("LOWER(login) = ?", login.downcase).first
- #end
- user
- end
- end
-
- def self.find_by_rss_key(key)
- Token.find_active_user('feeds', key)
- end
-
- def self.find_by_api_key(key)
- Token.find_active_user('api', key)
- end
-
- # Makes find_by_mail case-insensitive
- def self.find_by_mail(mail)
- where("LOWER(mail) = ?", mail.to_s.downcase).first
- end
-
- # Returns true if the default admin account can no longer be used
- def self.default_admin_account_changed?
- !User.active.find_by_login("admin").try(:check_password?, "admin")
- end
-
- def to_s
- name
- end
-
- CSS_CLASS_BY_STATUS = {
- STATUS_ANONYMOUS => 'anon',
- STATUS_ACTIVE => 'active',
- STATUS_REGISTERED => 'registered',
- STATUS_LOCKED => 'locked'
- }
-
- def css_classes
- "user #{CSS_CLASS_BY_STATUS[status]}"
- end
-
- # Returns the current day according to user's time zone
- def today
- if time_zone.nil?
- Date.today
- else
- Time.now.in_time_zone(time_zone).to_date
- end
- end
-
- # Returns the day of +time+ according to user's time zone
- def time_to_date(time)
- if time_zone.nil?
- time.to_date
- else
- time.in_time_zone(time_zone).to_date
- end
- end
-
- def logged?
- true
- end
-
- def anonymous?
- !logged?
- end
-
- # Returns user's membership for the given project
- # or nil if the user is not a member of project
- def membership(project)
- project_id = project.is_a?(Project) ? project.id : project
-
- @membership_by_project_id ||= Hash.new {|h, project_id|
- h[project_id] = memberships.where(:project_id => project_id).first
- }
- @membership_by_project_id[project_id]
- end
-
- def coursemembership(course)
- course_id = course.is_a?(Course) ? course.id : course
-
- @membership_by_course_id ||= Hash.new {|h, course_id|
- h[course_id] = coursememberships.where(:course_id => course_id).first
- }
- @membership_by_course_id[course_id]
- end
-
- # Return user's roles for project
- def roles_for_project(project)
- roles = []
- # No role on archived projects
- return roles if project.nil? || project.archived?
- if logged?
- # Find project membership
- membership = membership(project)
- if membership
- roles = membership.roles
- else
- @role_non_member ||= Role.non_member
- roles << @role_non_member
- end
- else
- @role_anonymous ||= Role.anonymous
- roles << @role_anonymous
- end
- roles
- end
-
- # 用户课程权限判断
- def roles_for_course(course)
- roles = []
- # No role on archived courses
- return roles if course.nil? || course.archived?
- if logged?
- # Find course membership
- membership = coursemembership(course)
- if membership
- roles = membership.roles
- else
- @role_non_member ||= Role.non_member
- roles << @role_non_member
- end
- else
- @role_anonymous ||= Role.anonymous
- roles << @role_anonymous
- end
- roles
- end
-
- # Return true if the user is a member of project
- def member_of?(project)
- projects.to_a.include?(project)
- end
-
- def member_of_course?(course)
- courses.to_a.include?(course)
- end
-
- def member_of_course_group?(course_group)
- course_groups.to_a.include?(course_group)
- end
- # Returns a hash of user's projects grouped by roles
- def projects_by_role
- return @projects_by_role if @projects_by_role
-
- @projects_by_role = Hash.new([])
- memberships.each do |membership|
- if membership.project
- membership.roles.each do |role|
- @projects_by_role[role] = [] unless @projects_by_role.key?(role)
- @projects_by_role[role] << membership.project
- end
- end
- end
- @projects_by_role.each do |role, projects|
- projects.uniq!
- end
-
- @projects_by_role
- end
-
- # 课程的角色权限
- def courses_by_role
- return @courses_by_role if @courses_by_role
-
- @courses_by_role = Hash.new([])
- coursememberships.each do |membership|
- if membership.course
- membership.roles.each do |role|
- @courses_by_role[role] = [] unless @courses_by_role.key?(role)
- @courses_by_role[role] << membership.course
- end
- end
- end
- @courses_by_role.each do |role, courses|
- courses.uniq!
- end
-
- @courses_by_role
- end
- # Returns true if user is arg or belongs to arg
- def is_or_belongs_to?(arg)
- if arg.is_a?(User)
- self == arg
- elsif arg.is_a?(Group)
- arg.users.include?(self)
- else
- false
- end
- end
-
-
- # Return true if the user is allowed to do the specified action on a specific context
- # Action can be:
- # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
- # * a permission Symbol (eg. :edit_project)
- # Context can be:
- # * a project : returns true if user is allowed to do the specified action on this project
- # * an array of projects : returns true if user is allowed on every project
- # * nil with options[:global] set : check if user has at least one role allowed for this action,
- # or falls back to Non Member / Anonymous permissions depending if the user is logged
- def allowed_to?(action, context, options={}, &block)
- if Project === context
- return false unless context.allows_to?(action)
- # Admin users are authorized for anything else
- return true if admin?
-
- roles = roles_for_project(context)
- return false unless roles
- roles.any? {|role|
- (context.is_public? || role.member?) &&
- role.allowed_to?(action) &&
- (block_given? ? yield(role, self) : true)
- }
- #添加课程相关的权限判断
- elsif Course === context
- return false unless context.allows_to?(action)
- # Admin users are authorized for anything else
- return true if admin?
-
- roles = roles_for_course(context)
- return false unless roles
- roles.any? {|role|
- (context.is_public? || role.member?) &&
- role.allowed_to?(action) &&
- (block_given? ? yield(role, self) : true)
- }
- elsif context && context.is_a?(Array)
- if context.empty?
- false
- else
- # Authorize if user is authorized on every element of the array
- context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
- end
- elsif options[:global]
- # Admin users are always authorized
- return true if admin?
-
- # authorize if user has at least one role that has this permission
- roles = memberships.collect {|m| m.roles}.flatten.uniq
- if roles.count == 0
- roles = coursememberships.collect {|m| m.roles}.flatten.uniq
- end
- roles << (self.logged? ? Role.non_member : Role.anonymous)
- roles.any? {|role|
- role.allowed_to?(action) &&
- (block_given? ? yield(role, self) : true)
- }
- else
- if admin?
- return true
- end
- #无项目时 查看Non member(id为1)角色是否有权限执行action
- Role.find('1').allowed_to?(action)
- # false
- end
- end
-
- # Is the user allowed to do the specified action on any project?
- # See allowed_to? for the actions and valid options.
- def allowed_to_globally?(action, options, &block)
- allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
- end
-
- # Returns true if the user is allowed to delete his own account
- def own_account_deletable?
- Setting.unsubscribe? &&
- (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
- end
-
- safe_attributes 'login',
- 'firstname',
- 'lastname',
- 'mail',
- 'mail_notification',
- 'language',
- 'custom_field_values',
- 'custom_fields',
- 'identity_url'
-
- safe_attributes 'status',
- 'auth_source_id',
- :if => lambda {|user, current_user| current_user.admin?}
-
- safe_attributes 'group_ids',
- :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
-
- # Utility method to help check if a user should be notified about an
- # event.
- #
- # TODO: only supports Issue events currently
- def notify_about?(object)
- if mail_notification == 'all'
- true
- elsif mail_notification.blank? || mail_notification == 'none'
- false
- else
- case object
- when Issue
- case mail_notification
- when 'selected', 'only_my_events'
- # user receives notifications for created/assigned issues on unselected projects
- object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
- when 'only_assigned'
- is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
- when 'only_owner'
- object.author == self
- end
- when News
- # always send to project members except when mail_notification is set to 'none'
- true
- #判定用户是否接受留言提醒邮件
- when JournalsForMessage
- ##如果是直接留言并且留言对象是Project并且Project类型是课程(课程留言)
- if !object.at_user && object.jour.class.to_s.to_sym == :Project && object.jour.project_type == 1
- #根据用户设置邮件接收模式判定当前用户是否接受邮件提醒
- is_notified_project object.jour
- end
-
- end
- end
- end
-
- #用户是否接收project的消息提醒
- def is_notified_project arg
- if arg.is_a?(Project)
- case mail_notification
- when 'selected'
- notified_projects_ids.include?(arg.id)
- when 'only_my_events'
- projects.include?(arg)
- when 'only_assigned'
- false
- when 'only_owner'
- course = Course.find_by_extra(arg.identifier)
- course.teacher == self
- end
- #勾选的项目或用户的项目 TODO:需改
- #notified_projects_ids.include?(arg) || projects.include?(arg)
- else
- false
- end
- end
-
- def self.current=(user)
- Thread.current[:current_user] = user
- end
-
- def self.current
- Thread.current[:current_user] ||= User.anonymous
- end
-
- # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
- # one anonymous user per database.
- def self.anonymous
- anonymous_user = AnonymousUser.first
- if anonymous_user.nil?
- anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
- raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
- end
- anonymous_user
- end
-
- # Salts all existing unsalted passwords
- # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
- # This method is used in the SaltPasswords migration and is to be kept as is
- def self.salt_unsalted_passwords!
- transaction do
- User.where("salt IS NULL OR salt = ''").find_each do |user|
- next if user.hashed_password.blank?
- salt = User.generate_salt
- hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
- User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
- end
- end
- end
-
- protected
-
- def validate_password_length
- # Password length validation based on setting
- if !password.nil? && password.size < Setting.password_min_length.to_i
- errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
- end
- end
- private
-
- def act_as_activity
- self.acts << Activity.new(:user_id => self.id)
- end
-
- # Removes references that are not handled by associations
- # Things that are not deleted are reassociated with the anonymous user
- def remove_references_before_destroy
- return if self.id.nil?
-
- substitute = User.anonymous
- Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- Notificationcomment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
- Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
- JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
- JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
- Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- # Remove private queries and keep public ones
- ::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
- ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
- TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
- Token.delete_all ['user_id = ?', id]
- Watcher.delete_all ['user_id = ?', id]
- WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
- end
-
- # Return password digest
- def self.hash_password(clear_password)
- Digest::SHA1.hexdigest(clear_password || "")
- end
-
- # Returns a 128bits random salt as a hex string (32 chars long)
- def self.generate_salt
- Redmine::Utils.random_hex(16)
- end
-
-
-
-end
-
-class AnonymousUser < User
- validate :validate_anonymous_uniqueness, :on => :create
-
- def validate_anonymous_uniqueness
- # There should be only one AnonymousUser in the database
- errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
- end
-
- def available_custom_fields
- []
- end
-
- # Overrides a few properties
- def logged?; false end
- def admin; false end
- def name(*args); I18n.t(:label_user_anonymous) end
- def mail; nil end
- def time_zone; nil end
- def rss_key; nil end
-
- def pref
- UserPreference.new(:user => self)
- end
-
- # def member_of?(project)
- # false
- # end
-
- # Anonymous user can not be destroyed
- def destroy
- false
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require "digest/sha1"
+
+class User < Principal
+ TEACHER = 0
+ STUDENT = 1
+ ENTERPRISE = 2
+ DEVELOPER = 3
+
+ include Redmine::SafeAttributes
+ seems_rateable_rater
+ # Different ways of displaying/sorting users
+ USER_FORMATS = {
+ :firstname_lastname => {
+ :string => '#{firstname} #{lastname}',
+ :order => %w(firstname lastname id),
+ :setting_order => 1
+ },
+ :firstname_lastinitial => {
+ :string => '#{firstname} #{lastname.to_s.chars.first}.',
+ :order => %w(firstname lastname id),
+ :setting_order => 2
+ },
+ :firstname => {
+ :string => '#{firstname}',
+ :order => %w(firstname id),
+ :setting_order => 3
+ },
+ :lastname_firstname => {
+ :string => '#{lastname} #{firstname}',
+ :order => %w(lastname firstname id),
+ :setting_order => 4
+ },
+ :lastname_coma_firstname => {
+ :string => '#{lastname}, #{firstname}',
+ :order => %w(lastname firstname id),
+ :setting_order => 5
+ },
+ :lastname => {
+ :string => '#{lastname}',
+ :order => %w(lastname id),
+ :setting_order => 6
+ },
+ :username => {
+ :string => '#{login}',
+ :order => %w(login id),
+ :setting_order => 7
+ },
+ }
+
+ #每日一报、一事一报、不报
+ MAIL_NOTIFICATION_OPTIONS = [
+ ['all', :label_user_mail_option_all],
+ #['week', :label_user_mail_option_week],
+ ['day', :label_user_mail_option_day],
+ ['none', :label_user_mail_option_none]
+ ]
+
+ has_many :homework_users
+ has_many :homework_attaches, :through => :homework_users
+ has_many :homework_evaluations
+
+ #问卷相关关关系
+ has_many :poll_users, :dependent => :destroy
+ has_many :poll_votes, :dependent => :destroy
+ has_many :poll, :dependent => :destroy #用户创建的问卷
+ has_many :answers, :source => :poll, :through => :poll_users, :dependent => :destroy #用户已经完成问答的问卷
+ # end
+
+ has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
+ :after_remove => Proc.new {|user, group| group.user_removed(user)}
+ has_many :changesets, :dependent => :nullify
+ has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
+ has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
+ has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
+ belongs_to :auth_source
+ belongs_to :ucourse, :class_name => 'Course', :foreign_key => :id #huang
+## added by xianbo for delete
+ has_many :biding_projects, :dependent => :destroy
+ has_many :contesting_projects, :dependent => :destroy
+ belongs_to :softapplication, :foreign_key => 'id', :dependent => :destroy
+##ended by xianbo
+
+#####fq
+ has_many :jours, :class_name => 'JournalsForMessage', :dependent => :destroy
+ has_many :journals_messages, :class_name => 'JournalsForMessage', :foreign_key => "user_id", :dependent => :destroy
+ has_many :bids, :foreign_key => 'author_id', :dependent => :destroy
+ has_many :contests, :foreign_key => 'author_id', :dependent => :destroy
+ has_many :softapplications, :foreign_key => 'user_id', :dependent => :destroy
+ has_many :journals_for_messages, :as => :jour, :dependent => :destroy
+ has_many :new_jours, :as => :jour, :class_name => 'JournalsForMessage', :conditions => "status=1"
+ has_many :journal_replies, :dependent => :destroy
+ has_many :activities, :dependent => :destroy
+ has_many :students_for_courses
+ #has_many :courses, :through => :students_for_courses, :source => :project
+ has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy
+ has_many :file_commit, :class_name => 'Attachment', :foreign_key => 'author_id', :conditions => "container_type = 'Project' or container_type = 'Version'"
+####
+# added by bai
+ has_many :join_in_contests, :dependent => :destroy
+ has_many :news, :foreign_key => 'author_id'
+ has_many :contestnotification, :foreign_key => 'author_id'
+ has_many :comments, :foreign_key => 'author_id'
+ has_many :notificationcomments, :foreign_key => 'author_id'
+ has_many :wiki_contents, :foreign_key => 'author_id'
+ has_many :journals
+ has_many :messages, :foreign_key => 'author_id'
+ has_one :user_score, :dependent => :destroy
+ has_many :documents # 项目中关联的文档再次与人关联
+# end
+
+######added by nie
+ has_many :project_infos, :dependent => :destroy
+ has_one :user_status, :dependent => :destroy
+ #####
+ has_many :shares ,:dependent => :destroy
+
+ # add by zjc
+ has_one :level, :class_name => 'UserLevels', :dependent => :destroy
+ has_many :memos , :foreign_key => 'author_id'
+ #####
+ scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") }
+ scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
+ scope :visible, lambda {|*args|
+ nil
+ }
+
+
+ acts_as_customizable
+ ############################added by william
+ acts_as_taggable
+ scope :by_join_date, order("created_on DESC")
+ ############################# added by liuping 关注
+ acts_as_watchable
+
+ has_one :user_extensions,:dependent => :destroy
+ ## end
+
+ # default_scope -> { includes(:user_extensions, :user_score) }
+ scope :teacher, -> {
+ joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::TEACHER)
+ }
+ scope :student, -> {
+ joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::STUDENT)
+ }
+ scope :developer, -> {
+ joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::DEVELOPER)
+ }
+ scope :enterprise, -> {
+ joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::ENTERPRISE)
+ }
+
+ attr_accessor :password, :password_confirmation
+ attr_accessor :last_before_login_on
+ # Prevents unauthorized assignments
+ attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
+
+ LOGIN_LENGTH_LIMIT = 25
+ MAIL_LENGTH_LIMIT = 60
+
+ validates_presence_of :login, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
+ validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false
+ validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false
+ # Login must contain letters, numbers, underscores only
+ validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i
+ validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT
+ validates_length_of :firstname, :maximum => 30
+ validates_length_of :lastname, :maximum => 30
+ validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true
+ validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true
+ validates_confirmation_of :password, :allow_nil => true
+ validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
+ validate :validate_password_length
+ # validates_email_realness_of :mail
+ before_create :set_mail_notification
+ before_save :update_hashed_password
+ before_destroy :remove_references_before_destroy
+ # added by fq
+ after_create :act_as_activity
+ # end
+
+ scope :in_group, lambda {|group|
+ group_id = group.is_a?(Group) ? group.id : group.to_i
+ where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
+ }
+ scope :not_in_group, lambda {|group|
+ group_id = group.is_a?(Group) ? group.id : group.to_i
+ where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id)
+ }
+ scope :sorted, lambda { order(*User.fields_for_order_statement)}
+
+ scope :like, lambda {|arg, type|
+ if arg.blank?
+ where(nil)
+ else
+ pattern = "%#{arg.to_s.strip.downcase}%"
+ #where(" LOWER(concat(lastname, firstname)) LIKE :p ", :p => pattern)
+ if type == "0"
+ where(" LOWER(login) LIKE '#{pattern}' ")
+ elsif type == "1"
+ where(" LOWER(concat(lastname, firstname)) LIKE '#{pattern}' ")
+ elsif type == "3"
+ where(" LOWER(concat(lastname, firstname,login)) LIKE '#{pattern}' ")
+ else
+ where(" LOWER(mail) LIKE '#{pattern}' ")
+ end
+ end
+ }
+
+
+ # ======================================================================
+
+ def extensions
+ self.user_extensions ||= UserExtensions.new
+ end
+
+ def user_score_attr
+ self.user_score ||= UserScore.new
+ end
+
+ # ======================================================================
+
+ #选择项目成员时显示的用户信息文字
+ def userInfo
+ if self.realname.gsub(' ','') == "" || self.realname.nil?
+ info = self.nickname;
+ else
+ info=self.nickname + ' (' + self.realname + ')';
+ end
+ info
+ end
+
+ ###添加留言 fq
+ def add_jour(user, notes, reference_user_id = 0, options = {})
+ if options.count == 0
+ self.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => reference_user_id, :status => true)
+ else
+ jfm = self.journals_for_messages.build(options)
+ jfm.save
+ jfm
+ end
+ end
+
+ # 判断用户是否加入了竞赛中 fq
+ def join_in_contest?(bid)
+ joined = JoinInContest.where('user_id = ? and bid_id =?', self.id, bid.id)
+ if joined.size > 0
+ true
+ else
+ false
+ end
+ end
+
+ ### fq
+ def join_in?(course)
+ joined = StudentsForCourse.where('student_id = ? and course_id = ?', self.id, course.id)
+ if joined.size > 0
+ true
+ else
+ false
+ end
+ end
+
+ def show_name
+ unless self.user_extensions.nil?
+ if self.user_extensions.identity == 2
+ firstname
+ else
+ lastname+firstname
+ end
+ else
+ lastname+firstname
+ end
+ end
+ ## end
+
+ def count_new_jour
+ count = self.new_jours.count
+ end
+
+ #added by nie
+ def count_new_journal_reply
+ count = self.journal_reply.count
+ end
+
+ def set_mail_notification
+ ##add byxianbo
+ thread=Thread.new do
+ self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
+ true
+ end
+ end
+
+ def update_hashed_password
+ # update hashed_password if password was set
+ if self.password && self.auth_source_id.blank?
+ salt_password(password)
+ end
+ end
+
+ alias :base_reload :reload
+ def reload(*args)
+ @name = nil
+ @projects_by_role = nil
+ @courses_by_role = nil
+ @membership_by_project_id = nil
+ base_reload(*args)
+ end
+
+ def mail=(arg)
+ write_attribute(:mail, arg.to_s.strip)
+ end
+
+ def identity_url=(url)
+ if url.blank?
+ write_attribute(:identity_url, '')
+ else
+ begin
+ write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
+ rescue OpenIdAuthentication::InvalidOpenId
+ # Invalid url, don't save
+ end
+ end
+ self.read_attribute(:identity_url)
+ end
+
+ VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
+ # VALID_EMAIL_REGEX = /^[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+(\.[0-9a-zA-Z_-]+)+$/
+ # Returns the user that matches provided login and password, or nil
+ #登录,返回用户名与密码匹配的用户
+ def self.try_to_login(login, password)
+ login = login.to_s.lstrip.rstrip
+ password = password.to_s
+
+ # Make sure no one can sign in with an empty login or password
+ return nil if login.empty? || password.empty?
+ if (login =~ VALID_EMAIL_REGEX)
+ user = find_by_mail(login)
+ else
+ user = find_by_login(login)
+ end
+ if user
+ # user is already in local database
+ #return nil unless user.active?
+ return nil unless user.check_password?(password)
+ else
+ # user is not yet registered, try to authenticate with available sources
+ attrs = AuthSource.authenticate(login, password)
+ if attrs
+ user = new(attrs)
+ user.login = login
+ user.language = Setting.default_language
+ if user.save
+ user.reload
+ logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
+ end
+ end
+ end
+ if user && !user.new_record?
+ last_login_on = user.last_login_on.nil? ? '' : user.last_login_on.to_s
+ user.update_column(:last_login_on, Time.now)
+ end
+ [user, last_login_on]
+ rescue => text
+ raise text
+ end
+
+
+ def self.try_to_autologin(key)
+ user = Token.find_active_user('autologin', key, Setting.autologin.to_i)
+ if user
+ user.update_column(:last_login_on, Time.now)
+ user
+ end
+ end
+
+ def self.name_formatter(formatter = nil)
+ USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname]
+ end
+
+ # Returns an array of fields names than can be used to make an order statement for users
+ # according to how user names are displayed
+ # Examples:
+ #
+ # User.fields_for_order_statement => ['users.login', 'users.id']
+ # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id']
+ def self.fields_for_order_statement(table=nil)
+ table ||= table_name
+ name_formatter[:order].map {|field| "#{table}.#{field}"}
+ end
+
+ # Return user's full name for display
+ def realname(formatter = nil)
+ f = self.class.name_formatter(formatter)
+ if formatter
+ eval('"' + f[:string] + '"')
+ else
+ @name ||= eval('"' + f[:string] + '"')
+ end
+ end
+
+ def nickname(formatter = nil)
+ login
+ end
+
+ def name(formatter = nil)
+ login
+ end
+
+ def active?
+ self.status == STATUS_ACTIVE
+ end
+
+ def registered?
+ self.status == STATUS_REGISTERED
+ end
+
+ def locked?
+ self.status == STATUS_LOCKED
+ end
+
+ def activate
+ self.status = STATUS_ACTIVE
+ end
+
+ def register
+ self.status = STATUS_REGISTERED
+ end
+
+ def lock
+ self.status = STATUS_LOCKED
+ end
+
+ def activate!
+ update_attribute(:status, STATUS_ACTIVE)
+ end
+
+ def register!
+ update_attribute(:status, STATUS_REGISTERED)
+ end
+
+ def lock!
+ update_attribute(:status, STATUS_LOCKED)
+ end
+
+ # Returns true if +clear_password+ is the correct user's password, otherwise false
+ def check_password?(clear_password)
+ if auth_source_id.present?
+ auth_source.authenticate(self.login, clear_password)
+ else
+ User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password
+ end
+ end
+ def check_password1?(clear_password)
+
+ clear_password == hashed_password
+
+ end
+ # Generates a random salt and computes hashed_password for +clear_password+
+ # The hashed password is stored in the following form: SHA1(salt + SHA1(password))
+ def salt_password(clear_password)
+ self.salt = User.generate_salt
+ self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}")
+ end
+
+ # Does the backend storage allow this user to change their password?
+ def change_password_allowed?
+ return true if auth_source.nil?
+ return auth_source.allow_password_changes?
+ end
+
+ # Generate and set a random password. Useful for automated user creation
+ # Based on Token#generate_token_value
+ #
+ def random_password
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ password = ''
+ 40.times { |i| password << chars[rand(chars.size-1)] }
+ self.password = password
+ self.password_confirmation = password
+ self
+ end
+
+ def pref
+ self.preference ||= UserPreference.new(:user => self)
+ end
+
+ def time_zone
+ @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
+ end
+
+ def wants_comments_in_reverse_order?
+ self.pref[:comments_sorting] == 'desc'
+ end
+
+ def wants_notificationcomments_in_reverse_order?
+ self.pref[:notificationcomments_sorting] == 'desc'
+ end
+ # Return user's RSS key (a 40 chars long string), used to access feeds
+ def rss_key
+ if rss_token.nil?
+ create_rss_token(:action => 'feeds')
+ end
+ rss_token.value
+ end
+
+ # Return user's API key (a 40 chars long string), used to access the API
+ def api_key
+ if api_token.nil?
+ create_api_token(:action => 'api')
+ end
+ api_token.value
+ end
+
+ # Return an array of project ids for which the user has explicitly turned mail notifications on
+ def notified_projects_ids
+ @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
+ end
+
+ def notified_project_ids=(ids)
+ Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
+ Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
+ @notified_projects_ids = nil
+ notified_projects_ids
+ end
+
+ def valid_notification_options
+ self.class.valid_notification_options(self)
+ end
+
+ # Only users that belong to more than 1 project can select projects for which they are notified
+ def self.valid_notification_options(user=nil)
+ # Note that @user.membership.size would fail since AR ignores
+ # :include association option when doing a count
+ if user.nil? || user.memberships.length < 1
+ MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
+ else
+ MAIL_NOTIFICATION_OPTIONS
+ end
+ end
+
+ # Find a user account by matching the exact login and then a case-insensitive
+ # version. Exact matches will be given priority.
+ #通过用户名查找相应的用户,若没有匹配到,则不区分大小写进行查询
+ #修改:不再匹配不区分大小写情况 -zjc
+ def self.find_by_login(login)
+ if login.present?
+ login = login.to_s
+ # First look for an exact match
+ user = where(:login => login).all.detect {|u| u.login == login}
+ #unless user
+ # # Fail over to case-insensitive if none was found
+ # user = where("LOWER(login) = ?", login.downcase).first
+ #end
+ user
+ end
+ end
+
+ def self.find_by_rss_key(key)
+ Token.find_active_user('feeds', key)
+ end
+
+ def self.find_by_api_key(key)
+ Token.find_active_user('api', key)
+ end
+
+ # Makes find_by_mail case-insensitive
+ def self.find_by_mail(mail)
+ where("LOWER(mail) = ?", mail.to_s.downcase).first
+ end
+
+ # Returns true if the default admin account can no longer be used
+ def self.default_admin_account_changed?
+ !User.active.find_by_login("admin").try(:check_password?, "admin")
+ end
+
+ def to_s
+ name
+ end
+
+ CSS_CLASS_BY_STATUS = {
+ STATUS_ANONYMOUS => 'anon',
+ STATUS_ACTIVE => 'active',
+ STATUS_REGISTERED => 'registered',
+ STATUS_LOCKED => 'locked'
+ }
+
+ def css_classes
+ "user #{CSS_CLASS_BY_STATUS[status]}"
+ end
+
+ # Returns the current day according to user's time zone
+ def today
+ if time_zone.nil?
+ Date.today
+ else
+ Time.now.in_time_zone(time_zone).to_date
+ end
+ end
+
+ # Returns the day of +time+ according to user's time zone
+ def time_to_date(time)
+ if time_zone.nil?
+ time.to_date
+ else
+ time.in_time_zone(time_zone).to_date
+ end
+ end
+
+ def logged?
+ true
+ end
+
+ def anonymous?
+ !logged?
+ end
+
+ # Returns user's membership for the given project
+ # or nil if the user is not a member of project
+ def membership(project)
+ project_id = project.is_a?(Project) ? project.id : project
+
+ @membership_by_project_id ||= Hash.new {|h, project_id|
+ h[project_id] = memberships.where(:project_id => project_id).first
+ }
+ @membership_by_project_id[project_id]
+ end
+
+ def coursemembership(course)
+ course_id = course.is_a?(Course) ? course.id : course
+
+ @membership_by_course_id ||= Hash.new {|h, course_id|
+ h[course_id] = coursememberships.where(:course_id => course_id).first
+ }
+ @membership_by_course_id[course_id]
+ end
+
+ # Return user's roles for project
+ def roles_for_project(project)
+ roles = []
+ # No role on archived projects
+ return roles if project.nil? || project.archived?
+ if logged?
+ # Find project membership
+ membership = membership(project)
+ if membership
+ roles = membership.roles
+ else
+ @role_non_member ||= Role.non_member
+ roles << @role_non_member
+ end
+ else
+ @role_anonymous ||= Role.anonymous
+ roles << @role_anonymous
+ end
+ roles
+ end
+
+ # 用户课程权限判断
+ def roles_for_course(course)
+ roles = []
+ # No role on archived courses
+ return roles if course.nil? || course.archived?
+ if logged?
+ # Find course membership
+ membership = coursemembership(course)
+ if membership
+ roles = membership.roles
+ else
+ @role_non_member ||= Role.non_member
+ roles << @role_non_member
+ end
+ else
+ @role_anonymous ||= Role.anonymous
+ roles << @role_anonymous
+ end
+ roles
+ end
+
+ # Return true if the user is a member of project
+ def member_of?(project)
+ projects.to_a.include?(project)
+ end
+
+ def member_of_course?(course)
+ courses.to_a.include?(course)
+ end
+
+ def member_of_course_group?(course_group)
+ course_groups.to_a.include?(course_group)
+ end
+ # Returns a hash of user's projects grouped by roles
+ def projects_by_role
+ return @projects_by_role if @projects_by_role
+
+ @projects_by_role = Hash.new([])
+ memberships.each do |membership|
+ if membership.project
+ membership.roles.each do |role|
+ @projects_by_role[role] = [] unless @projects_by_role.key?(role)
+ @projects_by_role[role] << membership.project
+ end
+ end
+ end
+ @projects_by_role.each do |role, projects|
+ projects.uniq!
+ end
+
+ @projects_by_role
+ end
+
+ # 课程的角色权限
+ def courses_by_role
+ return @courses_by_role if @courses_by_role
+
+ @courses_by_role = Hash.new([])
+ coursememberships.each do |membership|
+ if membership.course
+ membership.roles.each do |role|
+ @courses_by_role[role] = [] unless @courses_by_role.key?(role)
+ @courses_by_role[role] << membership.course
+ end
+ end
+ end
+ @courses_by_role.each do |role, courses|
+ courses.uniq!
+ end
+
+ @courses_by_role
+ end
+ # Returns true if user is arg or belongs to arg
+ def is_or_belongs_to?(arg)
+ if arg.is_a?(User)
+ self == arg
+ elsif arg.is_a?(Group)
+ arg.users.include?(self)
+ else
+ false
+ end
+ end
+
+
+ # Return true if the user is allowed to do the specified action on a specific context
+ # Action can be:
+ # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
+ # * a permission Symbol (eg. :edit_project)
+ # Context can be:
+ # * a project : returns true if user is allowed to do the specified action on this project
+ # * an array of projects : returns true if user is allowed on every project
+ # * nil with options[:global] set : check if user has at least one role allowed for this action,
+ # or falls back to Non Member / Anonymous permissions depending if the user is logged
+ def allowed_to?(action, context, options={}, &block)
+ if Project === context
+ return false unless context.allows_to?(action)
+ # Admin users are authorized for anything else
+ return true if admin?
+
+ roles = roles_for_project(context)
+ return false unless roles
+ roles.any? {|role|
+ (context.is_public? || role.member?) &&
+ role.allowed_to?(action) &&
+ (block_given? ? yield(role, self) : true)
+ }
+ #添加课程相关的权限判断
+ elsif Course === context
+ return false unless context.allows_to?(action)
+ # Admin users are authorized for anything else
+ return true if admin?
+
+ roles = roles_for_course(context)
+ return false unless roles
+ roles.any? {|role|
+ (context.is_public? || role.member?) &&
+ role.allowed_to?(action) &&
+ (block_given? ? yield(role, self) : true)
+ }
+ elsif context && context.is_a?(Array)
+ if context.empty?
+ false
+ else
+ # Authorize if user is authorized on every element of the array
+ context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&)
+ end
+ elsif options[:global]
+ # Admin users are always authorized
+ return true if admin?
+
+ # authorize if user has at least one role that has this permission
+ roles = memberships.collect {|m| m.roles}.flatten.uniq
+ if roles.count == 0
+ roles = coursememberships.collect {|m| m.roles}.flatten.uniq
+ end
+ roles << (self.logged? ? Role.non_member : Role.anonymous)
+ roles.any? {|role|
+ role.allowed_to?(action) &&
+ (block_given? ? yield(role, self) : true)
+ }
+ else
+ if admin?
+ return true
+ end
+ #无项目时 查看Non member(id为1)角色是否有权限执行action
+ Role.find('1').allowed_to?(action)
+ # false
+ end
+ end
+
+ # Is the user allowed to do the specified action on any project?
+ # See allowed_to? for the actions and valid options.
+ def allowed_to_globally?(action, options, &block)
+ allowed_to?(action, nil, options.reverse_merge(:global => true), &block)
+ end
+
+ # Returns true if the user is allowed to delete his own account
+ def own_account_deletable?
+ Setting.unsubscribe? &&
+ (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?)
+ end
+
+ safe_attributes 'login',
+ 'firstname',
+ 'lastname',
+ 'mail',
+ 'mail_notification',
+ 'language',
+ 'custom_field_values',
+ 'custom_fields',
+ 'identity_url'
+
+ safe_attributes 'status',
+ 'auth_source_id',
+ :if => lambda {|user, current_user| current_user.admin?}
+
+ safe_attributes 'group_ids',
+ :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
+
+ # Utility method to help check if a user should be notified about an
+ # event.
+ #
+ # TODO: only supports Issue events currently
+ def notify_about?(object)
+ if mail_notification == 'all'
+ true
+ elsif mail_notification.blank? || mail_notification == 'none'
+ false
+ else
+ case object
+ when Issue
+ case mail_notification
+ when 'selected', 'only_my_events'
+ # user receives notifications for created/assigned issues on unselected projects
+ object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
+ when 'only_assigned'
+ is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was)
+ when 'only_owner'
+ object.author == self
+ end
+ when News
+ # always send to project members except when mail_notification is set to 'none'
+ true
+ #判定用户是否接受留言提醒邮件
+ when JournalsForMessage
+ ##如果是直接留言并且留言对象是Project并且Project类型是课程(课程留言)
+ if !object.at_user && object.jour.class.to_s.to_sym == :Project && object.jour.project_type == 1
+ #根据用户设置邮件接收模式判定当前用户是否接受邮件提醒
+ is_notified_project object.jour
+ end
+
+ end
+ end
+ end
+
+ #用户是否接收project的消息提醒
+ def is_notified_project arg
+ if arg.is_a?(Project)
+ case mail_notification
+ when 'selected'
+ notified_projects_ids.include?(arg.id)
+ when 'only_my_events'
+ projects.include?(arg)
+ when 'only_assigned'
+ false
+ when 'only_owner'
+ course = Course.find_by_extra(arg.identifier)
+ course.teacher == self
+ end
+ #勾选的项目或用户的项目 TODO:需改
+ #notified_projects_ids.include?(arg) || projects.include?(arg)
+ else
+ false
+ end
+ end
+
+ def self.current=(user)
+ Thread.current[:current_user] = user
+ end
+
+ def self.current
+ Thread.current[:current_user] ||= User.anonymous
+ end
+
+ # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
+ # one anonymous user per database.
+ def self.anonymous
+ anonymous_user = AnonymousUser.first
+ if anonymous_user.nil?
+ anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
+ raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
+ end
+ anonymous_user
+ end
+
+ # Salts all existing unsalted passwords
+ # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password))
+ # This method is used in the SaltPasswords migration and is to be kept as is
+ def self.salt_unsalted_passwords!
+ transaction do
+ User.where("salt IS NULL OR salt = ''").find_each do |user|
+ next if user.hashed_password.blank?
+ salt = User.generate_salt
+ hashed_password = User.hash_password("#{salt}#{user.hashed_password}")
+ User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password)
+ end
+ end
+ end
+
+ protected
+
+ def validate_password_length
+ # Password length validation based on setting
+ if !password.nil? && password.size < Setting.password_min_length.to_i
+ errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
+ end
+ end
+ private
+
+ def act_as_activity
+ self.acts << Activity.new(:user_id => self.id)
+ end
+
+ # Removes references that are not handled by associations
+ # Things that are not deleted are reassociated with the anonymous user
+ def remove_references_before_destroy
+ return if self.id.nil?
+
+ substitute = User.anonymous
+ Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ Notificationcomment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
+ Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
+ JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
+ JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
+ Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ # Remove private queries and keep public ones
+ ::Query.delete_all ['user_id = ? AND is_public = ?', id, false]
+ ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
+ TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
+ Token.delete_all ['user_id = ?', id]
+ Watcher.delete_all ['user_id = ?', id]
+ WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
+ end
+
+ # Return password digest
+ def self.hash_password(clear_password)
+ Digest::SHA1.hexdigest(clear_password || "")
+ end
+
+ # Returns a 128bits random salt as a hex string (32 chars long)
+ def self.generate_salt
+ Redmine::Utils.random_hex(16)
+ end
+
+
+
+end
+
+class AnonymousUser < User
+ validate :validate_anonymous_uniqueness, :on => :create
+
+ def validate_anonymous_uniqueness
+ # There should be only one AnonymousUser in the database
+ errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists?
+ end
+
+ def available_custom_fields
+ []
+ end
+
+ # Overrides a few properties
+ def logged?; false end
+ def admin; false end
+ def name(*args); I18n.t(:label_user_anonymous) end
+ def mail; nil end
+ def time_zone; nil end
+ def rss_key; nil end
+
+ def pref
+ UserPreference.new(:user => self)
+ end
+
+ # def member_of?(project)
+ # false
+ # end
+
+ # Anonymous user can not be destroyed
+ def destroy
+ false
+ end
+end
diff --git a/app/models/wiki_content_observer.rb b/app/models/wiki_content_observer.rb
index 6e13d1a8a..3ded4da86 100644
--- a/app/models/wiki_content_observer.rb
+++ b/app/models/wiki_content_observer.rb
@@ -1,28 +1,28 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-class WikiContentObserver < ActiveRecord::Observer
- def after_create(wiki_content)
- Mailer.run.wiki_content_added(wiki_content) if Setting.notified_events.include?('wiki_content_added')
- end
-
- def after_update(wiki_content)
- if wiki_content.text_changed?
- Mailer.run.wiki_content_updated(wiki_content) if Setting.notified_events.include?('wiki_content_updated')
- end
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+class WikiContentObserver < ActiveRecord::Observer
+ def after_create(wiki_content)
+ Mailer.run.wiki_content_added(wiki_content) if Setting.notified_events.include?('wiki_content_added')
+ end
+
+ def after_update(wiki_content)
+ if wiki_content.text_changed?
+ Mailer.run.wiki_content_updated(wiki_content) if Setting.notified_events.include?('wiki_content_updated')
+ end
+ end
+end
diff --git a/app/models/zip_pack.rb b/app/models/zip_pack.rb
index e2d03f363..df6ad593f 100644
--- a/app/models/zip_pack.rb
+++ b/app/models/zip_pack.rb
@@ -1,18 +1,18 @@
-class ZipPack < ActiveRecord::Base
- # attr_accessible :title, :body
-
- def self.packed?(bid_id, user_id, digests)
- zip_pack = ZipPack.where(homework_id: bid_id, user_id: user_id).first
- return false unless zip_pack && zip_pack.digests == digests
- zip_pack
- end
-
- def file_valid?
- return false unless File.exist?(self.file_path)
- Trustie::Utils.digest(self.file_path) == self.file_digest
- end
-
- def digests
- self.file_digests.split(',').sort
- end
-end
+class ZipPack < ActiveRecord::Base
+ # attr_accessible :title, :body
+
+ def self.packed?(bid_id, user_id, digests)
+ zip_pack = ZipPack.where(homework_id: bid_id, user_id: user_id).first
+ return false unless zip_pack && zip_pack.digests == digests
+ zip_pack
+ end
+
+ def file_valid?
+ return false unless File.exist?(self.file_path)
+ Trustie::Utils.digest(self.file_path) == self.file_digest
+ end
+
+ def digests
+ self.file_digests.split(',').sort
+ end
+end
diff --git a/app/views/attachments/_form.html.erb b/app/views/attachments/_form.html.erb
index 18586e809..7e86b098b 100644
--- a/app/views/attachments/_form.html.erb
+++ b/app/views/attachments/_form.html.erb
@@ -1,11 +1,12 @@
+
<% if defined?(container) && container && container.saved_attachments %>
<% container.attachments.each_with_index do |attachment, i| %>
- <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
- <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 254, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") %>
- <%= l(:field_is_public)%>:
- <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public,attachment.is_public == 1 ? true : false,:class => 'is_public')%>
+ <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly => 'readonly') %>
+ <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 254, :placeholder => l(:label_optional_description), :class => 'description', :style => "display: inline-block;") %>
+ <%= l(:field_is_public) %>:
+ <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public, attachment.is_public == 1 ? true : false, :class => 'is_public') %>
<%= if attachment.id.nil?
#待补充代码
else
@@ -17,48 +18,64 @@
<%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
<% end %>
+ <% container.saved_attachments.each_with_index do |attachment, i| %>
+
+ <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly => 'readonly') %>
+ <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 254, :placeholder => l(:label_optional_description), :class => 'description', :style => "display: inline-block;") %>
+ <%= l(:field_is_public) %>:
+ <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public, attachment.is_public == 1 ? true : false, :class => 'is_public') %>
+ <%= if attachment.id.nil?
+ #待补充代码
+ else
+ link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload')
+ end
+ %>
+ <%#= render :partial => 'tags/tag', :locals => {:obj => attachment, :object_flag => "6"} %>
+ <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
+
+ <% end %>
<% end %>
-
-<% project = project %>
-
+
+ <% project = project %>
+
<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
- <%= button_tag l(:button_browse), :type=>"button", :onclick=>"_file.click()",:onmouseover => 'this.focus()', :style => ie8? ? 'display:none' : '' %>
+ <%= button_tag "文件浏览", :type=>"button", :onclick=>"$('#_file').click();",:onmouseover => 'this.focus()',:class => 'sub_btn' %>
<%= file_field_tag 'attachments[dummy][file]',
- :id => '_file',
- :class => 'file_selector',
- :multiple => true,
- :onchange => 'addInputFiles(this);',
- :style => ie8? ? '' : 'display:none',
- :data => {
- :max_file_size => Setting.attachment_max_size.to_i.kilobytes,
- :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
- :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
- :upload_path => uploads_path(:format => 'js',:project =>project),
- :description_placeholder => l(:label_optional_description),
- :field_is_public => l(:field_is_public),
- :are_you_sure => l(:text_are_you_sure),
- :file_count => l(:label_file_count),
- :delete_all_files => l(:text_are_you_sure_all)
- } %>
+ :id => '_file',
+ :class => 'file_selector',
+ :multiple => true,
+ :onchange => 'addInputFiles(this);',
+ :style => ie8? ? '' : 'display:none',
+ :data => {
+ :max_file_size => Setting.attachment_max_size.to_i.kilobytes,
+ :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
+ :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
+ :upload_path => uploads_path(:format => 'js', :project => project),
+ :description_placeholder => l(:label_optional_description),
+ :field_is_public => l(:field_is_public),
+ :are_you_sure => l(:text_are_you_sure),
+ :file_count => l(:label_file_count),
+ :delete_all_files => l(:text_are_you_sure_all)
+ } %>
- <%= l(:label_no_file_uploaded)%>
+ <%= l(:label_no_file_uploaded) %>
(<%= l(:label_max_size) %>:
<%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
-<% content_for :header_tags do %>
- <%= javascript_include_tag 'attachments' %>
-<% end %>
-
+ <% content_for :header_tags do %>
+ <%= javascript_include_tag 'attachments' %>
+ <% end %>
+
diff --git a/app/views/attachments/_form_project.html.erb b/app/views/attachments/_form_project.html.erb
new file mode 100644
index 000000000..9778d3242
--- /dev/null
+++ b/app/views/attachments/_form_project.html.erb
@@ -0,0 +1,67 @@
+
+<% if defined?(container) && container && container.saved_attachments %>
+ <% if isReply %>
+ <% container.saved_attachments.each_with_index do |attachment, i| %>
+
+ <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
+ <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") +
+ link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload') %>
+ <%#= render :partial => 'tags/tag', :locals => {:obj => attachment, :object_flag => "6"} %>
+ <%= l(:field_is_public)%>:
+ <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public,attachment.is_public == 1 ? true : false, :class => 'is_public')%>
+ <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
+
+ <% end %>
+ <% else %>
+ <% container.attachments.each_with_index do |attachment, i| %>
+
+ <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
+ <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") +
+ link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload') %>
+ <%#= render :partial => 'tags/tag', :locals => {:obj => attachment, :object_flag => "6"} %>
+ <%= l(:field_is_public)%>:
+ <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public,attachment.is_public == 1 ? true : false, :class => 'is_public')%>
+ <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
+
+ <% end %>
+ <% end %>
+<% end %>
+
+
+
+<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
+
+ <%= button_tag "文件浏览", :type=>"button", :onclick=>"_file.click()", :class =>"sub_btn",:style => ie8? ? 'display:none' : '' %>
+ <%= file_field_tag 'attachments[dummy][file]',
+ :id => '_file',
+ :class => 'file_selector',
+ :multiple => true,
+ :onchange => 'addInputFiles(this);',
+ :style => 'display:none',
+ :data => {
+ :max_file_size => Setting.attachment_max_size.to_i.kilobytes,
+ :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
+ :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
+ :upload_path => uploads_path(:format => 'js'),
+ :description_placeholder => l(:label_optional_description),
+ :field_is_public => l(:field_is_public),
+ :are_you_sure => l(:text_are_you_sure),
+ :file_count => l(:label_file_count),
+ :delete_all_files => l(:text_are_you_sure_all)
+ } %>
+ <%= l(:label_no_file_uploaded)%>
+(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
+
+
+<% content_for :header_tags do %>
+ <%= javascript_include_tag 'attachments' %>
+<% end %>
+
+
diff --git a/app/views/attachments/_links.html.erb b/app/views/attachments/_links.html.erb
index 02c7ba4dc..c3ec2b9f0 100644
--- a/app/views/attachments/_links.html.erb
+++ b/app/views/attachments/_links.html.erb
@@ -60,7 +60,7 @@
<% if defined?(thumbnails) && thumbnails %>
<% images = attachments.select(&:thumbnailable?) %>
<% if images.any? %>
-
+
<% images.each do |attachment| %>
<%= thumbnail_tag(attachment) %>
<% end %>
diff --git a/app/views/attachments/_project_file_links.html.erb b/app/views/attachments/_project_file_links.html.erb
new file mode 100644
index 000000000..9a65556d8
--- /dev/null
+++ b/app/views/attachments/_project_file_links.html.erb
@@ -0,0 +1,72 @@
+
+ <% is_float ||= false %>
+ <% for attachment in attachments %>
+
+ <%if is_float%>
+
+ <% end%>
+
+ <% if options[:length] %>
+ <%= link_to_short_attachment attachment, :class => ' link_file_board', :download => true,:length => options[:length] -%>
+ <% else %>
+ <%= link_to_short_attachment attachment, :class => ' link_file_board', :download => true -%>
+ <% end %>
+
+ <%if is_float%>
+
+ <% end%>
+
+ <% if attachment.is_text? %>
+ <%= link_to image_tag('magnifier.png'),
+ :controller => 'attachments',
+ :action => 'show',
+ :id => attachment,
+ :filename => attachment.filename%>
+ <% end %>
+
+ <%= h(truncate(" - #{attachment.description}", length: options[:length] ? options[:length]:15, omission: '...')) unless attachment.description.blank? %>
+
+
(
+ <%= number_to_human_size attachment.filesize %>)
+
+ <% if options[:deletable] %>
+ <% if attachment.container_type == 'HomeworkAttach' %>
+ <%= link_to image_tag('delete.png'), {:controller => 'attachments', :action => 'delete_homework', :id => attachment.id},
+ :data => {:confirm => l(:text_are_you_sure)},
+ :method => :delete,
+ :class => 'delete delete-homework-icon',
+ :remote => true,
+ :title => l(:button_delete) %>
+ <% else %>
+ <%= link_to image_tag('delete.png'), attachment_path(attachment),
+ :data => {:confirm => l(:text_are_you_sure)},
+ :method => :delete,
+ :class => 'delete',
+ #:remote => true,
+ #:id => "attachments_" + attachment.id.to_s,
+ :title => l(:button_delete) %>
+ <% end %>
+ <% end %>
+ <% if options[:wrap] %>
+
+
+ <% end %>
+ <% if options[:author] %>
+
+ <%= link_to h(truncate(attachment.author.name, length: 10, omission: '...')),user_path(attachment.author),:class => "c_orange" %>,
+ <%= format_time(attachment.created_on) %>
+
+ <% end %>
+
+ <% end %>
+
+ <% if defined?(thumbnails) && thumbnails %>
+ <% images = attachments.select(&:thumbnailable?) %>
+ <% if images.any? %>
+ <% images.each do |attachment| %>
+
<%= thumbnail_issue_tag(attachment) %>
+ <% end %>
+ <% end %>
+ <% end %>
+
+
diff --git a/app/views/avatar/_avatar_form.html.erb b/app/views/avatar/_avatar_form.html.erb
index 6f9b9c7b1..fef9f7bdb 100644
--- a/app/views/avatar/_avatar_form.html.erb
+++ b/app/views/avatar/_avatar_form.html.erb
@@ -46,7 +46,8 @@
<%= link_to l(:button_delete_file),{:controller => :avatar,:action => :delete_image,:remote=>true,:source_type=> source.class,:source_id=>source.id},:confirm => l(:text_are_you_sure), :method => :post, :class => "btn_addPic", :style => "text-decoration:none;" %>
<%= l(:button_upload_photo) %>
-
+
+
(个人头像建议90*90大小,课程和项目logo建议60*60大小,或者等比图像)
<%= file_field_tag 'avatar[image]',
diff --git a/app/views/avatar/_new_avatar_form.html.erb b/app/views/avatar/_new_avatar_form.html.erb
index 8ca08c766..8b81ebf66 100644
--- a/app/views/avatar/_new_avatar_form.html.erb
+++ b/app/views/avatar/_new_avatar_form.html.erb
@@ -2,7 +2,8 @@
<%= image_tag(url_to_avatar(source), id: "avatar_image", :width =>"60", :height =>"60",:alt=>"上传图片")%>
<%#= link_to l(:button_delete_file),{:controller => :avatar,:action => :delete_image,:remote=>true,:source_type=> source.class,:source_id=>source.id},:confirm => l(:text_are_you_sure), :method => :post, :class => "upbtn fl" %>
-上传图片
+<%= l(:button_upload_photo) %>
+(个人头像建议90*90大小,课程和项目logo建议60*60大小,或者等比图像)
<%= file_field_tag 'avatar[image]',
:id => nil,
:class => 'upload_file ',
diff --git a/app/views/bids/_alert_anonyoms.html.erb b/app/views/bids/_alert_anonyoms.html.erb
index 6467b3f27..2f1c443f4 100644
--- a/app/views/bids/_alert_anonyoms.html.erb
+++ b/app/views/bids/_alert_anonyoms.html.erb
@@ -39,28 +39,28 @@
<% if @bid.comment_status == 0%>
开启匿评功能
- 开启匿评后学生将不能对作业进行
+ 开启匿评后学生将不能对作品进行
修改、删除
等操作,目前有
<%= totle_size%>个
学生,共提交了
<%= cur_size %>
- 份作业,占
+ 份作品,占
<%= percent %>% ,
是否确定开启匿评?
<% elsif @bid.comment_status == 1 %>
关闭匿评功能
- 关闭匿评后学生将不能对作业进行
+ 关闭匿评后学生将不能对作品进行
匿评
- ,且作业列表将会
+ ,且作品列表将会
公开 ,
目前分配了
<%= totle_size%>份
- 匿评作业,已评了
+ 匿评作品,已评了
<%= cur_size %>
- 份作业,占
+ 份作品,占
<%= percent %>% ,
是否确定关闭匿评?
diff --git a/app/views/bids/_bid_homework_show.html.erb b/app/views/bids/_bid_homework_show.html.erb
index 6cb975055..2f2dbcd07 100644
--- a/app/views/bids/_bid_homework_show.html.erb
+++ b/app/views/bids/_bid_homework_show.html.erb
@@ -10,7 +10,7 @@
var hour=Math.floor((leftsecond-day1*24*60*60)/3600);
var minute=Math.floor((leftsecond-day1*24*60*60-hour*3600)/60);
var second=Math.floor(leftsecond-day1*24*60*60-hour*3600-minute*60);
- $("#"+divname).html("作业提交还剩 : "
+ $("#"+divname).html("作品提交还剩 : "
+day1+" 天 "
+hour+" 时 "
+minute+" 分 "
diff --git a/app/views/bids/_new_homework_form.html.erb b/app/views/bids/_new_homework_form.html.erb
index aeb9a5c41..16f78491b 100644
--- a/app/views/bids/_new_homework_form.html.erb
+++ b/app/views/bids/_new_homework_form.html.erb
@@ -1,53 +1,54 @@
-<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', :media => 'all' %>
-<%= error_messages_for 'bid' %>
-<%= hidden_field_tag 'course_id', @course.id %>
-
-
<%= l(:label_course_homework_new)%>
-
-
-
-
- * <%= l(:field_name)%> :
-
-
-
-
- <%= l(:field_quote)%> :
-
- <% if edit_mode %>
- <%= f.kindeditor :description,:width=>'91%',:editor_id => 'bid_description_editor',:owner_id => bid.id,:owner_type =>OwnerTypeHelper::BID %>
- <% else %>
- <%= hidden_field_tag :asset_id,params[:asset_id],:required => false,:style => 'display:none' %>
- <%= f.kindeditor :description,:width=>'91%',:editor_id => 'bid_description_editor' %>
- <% end %>
-
-
-
- * <%= l(:label_limit_time)%> :
-
- <%= calendar_for('bid_deadline')%>
-
-
- <%= l(:label_open_anonymous_evaluation)%> :
- >
-
-
-
- * <%= l(:field_evaluation_num)%> :
-
- <%= l(:label_evaluation_description)%>
-
-
-
- <%= l(:label_attachment_plural) %> :
- <%= render :partial => 'attachments/new_form', :locals => {:container => bid} %>
-
-
-
- <%= l(:button_create)%>
- <%= link_to l(:button_cancel), homework_course_path(@course), :class => "blue_btn grey_btn fl c_white"%>
-
-
-
-
-
+<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', :media => 'all' %>
+<%= error_messages_for 'bid' %>
+<%= hidden_field_tag 'course_id', @course.id %>
+
+
<%= l(:label_course_homework_new)%>
+
+
+
+
+ * <%= l(:field_name)%> :
+
+
+
+
+ <%= l(:field_quote)%> :
+
+ <% if edit_mode %>
+ <%= f.kindeditor :description,:width=>'91%',:editor_id => 'bid_description_editor',:owner_id => bid.id,:owner_type =>OwnerTypeHelper::BID %>
+ <% else %>
+ <%= hidden_field_tag :asset_id,params[:asset_id],:required => false,:style => 'display:none' %>
+ <%= f.kindeditor :description,:width=>'91%',:editor_id => 'bid_description_editor' %>
+ <% end %>
+
+
+
+ * <%= l(:label_limit_time)%> :
+
+ <%= calendar_for('bid_deadline')%>
+
+
+
+ <%= l(:label_open_anonymous_evaluation)%> :
+ >
+
+
+
+ * <%= l(:field_evaluation_num)%> :
+
+ <%= l(:label_evaluation_description)%>
+
+
+
+ <%= l(:label_attachment_plural) %> :
+ <%= render :partial => 'attachments/new_form', :locals => {:container => bid} %>
+
+
+
+ <%= l(:button_create)%>
+ <%= link_to l(:button_cancel), homework_course_path(@course), :class => "blue_btn grey_btn fl c_white"%>
+
+
+
+
+
diff --git a/app/views/boards/_form.html.erb b/app/views/boards/_form.html.erb
index c1ab085cf..47ae0672d 100644
--- a/app/views/boards/_form.html.erb
+++ b/app/views/boards/_form.html.erb
@@ -13,3 +13,18 @@
<% end %>
+
+
+
+
+ <%= link_to h(topic.subject), board_message_path(@board, topic), title:topic.subject.to_s, :class =>"problem_tit fl" %>
+ <% if topic.sticky? %>
+
<%= l(:label_board_sticky)%>
+ <% end %>
+
+ <%= l(:label_post_by)%><%= link_to topic.author, user_path(topic.author), :class =>"problem_name" %>
+ <%= l(:label_post_by_time)%><%= format_time topic.created_on %>
+
+
<%= link_to (l(:label_short_reply) + " "+topic.replies_count.to_s), board_message_path(@board, topic), :style =>"color:#fff;line-height: 18px;" %>
+
+
\ No newline at end of file
diff --git a/app/views/boards/_project_show.html.erb b/app/views/boards/_project_show.html.erb
index 765cb2a65..7c6594006 100644
--- a/app/views/boards/_project_show.html.erb
+++ b/app/views/boards/_project_show.html.erb
@@ -1,4 +1,4 @@
-
-
+
+
+ <% if User.current.language == "zh"%>
+ <%= h @board.name %>
+ <% else %>
+ <%= l(:project_module_boards) %>
+ <% end %>
+
+
+
<% if User.current.logged? %>
-
-
<%= l(:project_module_boards_post) %>
-
<%= form_for @message, :url => new_board_message_path(@board), :html => {:multipart => true, :id => 'message-form'} do |f| %>
- <%= render :partial => 'messages/form', :locals => {:f => f} %>
+
<%= l(:project_module_boards_post) %>
+ <%= render :partial => 'messages/form_project', :locals => {:f => f} %>
- <%= l(:button_submit)%>
- <%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;', :class => 'ButtonColor m3p10' %>
+ <%= l(:button_submit)%>
+ <%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;', :class => 'grey_btn ml10 fl' %>
<% end %>
+
<% end %>
<% if !User.current.logged? %>
-
- <% if @project.project_type == 1 %>
- <%= l(:label_user_login_course_board) %>
- <% else %>
- <%= l(:label_user_login_project_board) %>
- <% end %>
- <%= link_to l(:label_user_login_new), signin_path %>
-
+
+ <%= l(:label_user_login_project_board) %>
+ <%= link_to l(:label_user_login_new), signin_path, :class => "c_blue ml5" %>
<% end %>
-
-
-
- <% if User.current.language == "zh"%>
- <%= h @board.name %>
- <% else %>
- <%= l(:project_module_boards) %>
- <% end %>
-
-
-
-
<%= l(:label_project_board_count , :count => @topic_count)%>
- <% if @project.enabled_modules.where("name = 'boards'").count > 0 && User.current.member_of?(@project) %>
+
+
<%= l(:label_project_board_count , :count => @topic_count)%>
+<% if @project.enabled_modules.where("name = 'boards'").count > 0 && User.current.member_of?(@project) %>
<%= link_to l(:project_module_boards_post), new_board_message_path(@board),
- :class => 'problem_new_btn fl',
- :onclick => 'showAndScrollTo("add-message", "message_subject"); return false;' if User.current.logged? %>
- <% end %>
+ :class => 'problem_new_btn fl c_dorange',
+ :onclick => 'showAndScrollTo("add-message", "message_subject"); return false;' if User.current.logged? %>
+ <% end %>
-
+
- <% if @topics.any? %>
- <% @topics.each do |topic| %>
+<% if @topics.any? %>
+ <% @topics.each do |topic| %>
-
- <%= link_to image_tag(url_to_avatar(topic.author), :class => "problem_pic talk_pic fl"), user_path(topic.author) %>
-
+ <%= link_to image_tag(url_to_avatar(topic.author), :width=>"32",:height=>"32"), user_path(topic.author),:class => 'problem_pic talk_pic fl' %>
-
-
<%= link_to h(topic.subject), board_message_path(@board, topic), title:topic.subject.to_s, :class =>"problem_tit fl" %>
+ <%= link_to h(topic.subject), board_message_path(@board, topic), title:topic.subject.to_s, :class =>"problem_tit fl" %>
<% if topic.sticky? %>
-
<%= l(:label_board_sticky)%>
+
<%= l(:label_board_sticky)%>
<% end %>
-
-
- <%= l(:label_post_by)%><%= link_to topic.author, user_path(topic.author), :class =>"problem_name" %>
- <%= l(:label_post_by_time)%><%= format_time topic.created_on %>
-
+
+ <%= l(:label_post_by)%><%= link_to topic.author, user_path(topic.author), :class =>"problem_name" %>
+ <%= l(:label_post_by_time)%><%= format_time topic.created_on %>
-
<%= link_to (l(:label_short_reply) + " "+topic.replies_count.to_s), board_message_path(@board, topic), :style =>"color:#fff;line-height: 18px;" %>
+ <%= link_to (l(:label_short_reply) + " "+topic.replies_count.to_s), board_message_path(@board, topic), :class => "talk_btn fr c_white" %>
<% end %>
-
- <%= pagination_links_full @topic_pages, @topic_count, :per_page_links => false, :remote => false, :flag => true %>
-
<% else %>
<%= l(:label_no_data) %>
<% end %>
-
-
-
+
+ <%= pagination_links_full @topic_pages, @topic_count, :per_page_links => false, :remote => false, :flag => true %>
+
+
-<% other_formats_links do |f| %>
- <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
-<% end %>
+<%# other_formats_links do |f| %>
+ <%#= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
+<%# end %>
<% html_title @board.name %>
diff --git a/app/views/boards/show.html.erb b/app/views/boards/show.html.erb
index 81d1288fd..c9a8645de 100644
--- a/app/views/boards/show.html.erb
+++ b/app/views/boards/show.html.erb
@@ -1,6 +1,6 @@
-
-<% if @project %>
- <%= render :partial => 'project_show', locals: {project: @project} %>
-<% elsif @course %>
- <%= render :partial => 'course_show', locals: {course: @course} %>
-<% end %>
+
+<% if @project %>
+ <%= render :partial => 'project_show', locals: {project: @project} %>
+<% elsif @course %>
+ <%= render :partial => 'course_show', locals: {course: @course} %>
+<% end %>
diff --git a/app/views/common/_project_tab.html.erb b/app/views/common/_project_tab.html.erb
new file mode 100644
index 000000000..95ac48c59
--- /dev/null
+++ b/app/views/common/_project_tab.html.erb
@@ -0,0 +1,28 @@
+<% selected_tab = params[:tab] ? params[:tab].to_s : tabs.first[:name] %>
+
+
+
+ <% tabs.each do |tab| -%>
+ <%= link_to l(tab[:label]), { :tab => tab[:name] },
+ :id => "tab-#{tab[:name]}",
+ :class => (tab[:name] != selected_tab ? 'hwork_normaltab' : 'hwork_hovertab'),
+ :onclick => "showTab('#{tab[:name]}'); this.blur(); return false;" %>
+ <% end -%>
+
+
+
+
+
+
+<% tabs.each do |tab| -%>
+ <%= content_tag('div', render(:partial => tab[:partial], :locals => {:tab => tab} ),
+ :id => "tab-content-#{tab[:name]}",
+ :style => (tab[:name] != selected_tab ? 'display:none' : nil),
+ :class => 'hwork_normaltab') %>
+<% end -%>
diff --git a/app/views/courses/_course.html.erb b/app/views/courses/_course.html.erb
index 8974c3089..0e1b5caa6 100644
--- a/app/views/courses/_course.html.erb
+++ b/app/views/courses/_course.html.erb
@@ -79,16 +79,6 @@
-
- <%= content_tag "span","#{l(:label_duration_time)}:", :class => "course-font"%>
- <%= get_course_term @course %>
-
-
-
- <%= content_tag "span", "#{l(:label_course_brief_introduction)}:", :class => "course-font" %>
- <%= content_tag "div", course.short_description, :class => "brief_introduction", :title => course.short_description %>
-
-
-
-
-
-<% if @issue.safe_attribute? 'custom_field_values' %>
-<%= render :partial => 'issues/form_custom_fields' %>
-<% end %>
+
+
+ <%= l(:label_change_properties) %>
+
+
+ * <%= l(:field_status) %>:
+ <% if @issue.safe_attribute?('status_id') && @allowed_statuses.present? %>
+ <%= f.select :status_id,
+ (@allowed_statuses.collect { |p| [p.name, p.id] }),
+ {:no_label => true},
+ # ajax 刷新
+ #:onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')",
+ :class => "w150" %>
+ <% else %>
+ <%= h(@issue.status.name) %>
+ <% end %>
+
+
+
+ * <%= l(:field_priority) %>:
+ <% if @issue.safe_attribute? 'priority_id' %>
+ <%= f.select :priority_id,
+ (@priorities.collect { |p| [p.name, p.id] }),
+ {:required => true, :no_label => true}, :disabled => !@issue.leaf?,
+ :class => "w150" %>
+ <% end %>
+
+
+
+
+ <%= l(:field_assigned_to) %>:
+ <% if @issue.safe_attribute? 'assigned_to_id' %>
+ <%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to),
+ {:required => @issue.required_attribute?('assigned_to_id'), :no_label => true},
+ :class => "w150" %>
+ <% end %>
+
+
+
+ <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %>
+ <%= l(:field_fixed_version) %>:
+ <%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version),
+ {:include_blank => true, :required => @issue.required_attribute?('fixed_version_id'), :no_label => true},
+ :class => "w150" %>
+ <%#= link_to(image_tag('add.png', :style => 'vertical-align: middle;'),
+ new_project_version_path(@issue.project),
+ :remote => true,
+ :method => 'get',
+ :title => l(:label_version_new),
+ :tabindex => 200) if User.current.allowed_to?(:manage_versions, @issue.project) %>
+ <%= link_to "", new_project_version_path(@issue.project), :class => "pic_add mt5 ml5 " %>
+
+ <% end %>
+
+
+
+
+
+
+ <%= l(:field_start_date) %>:
+ <% if @issue.safe_attribute? 'start_date' %>
+ <%= f.text_field :start_date,
+ :size => 22,
+ :disabled => !@issue.leaf?,
+ :no_label => true,
+ :required => @issue.required_attribute?('start_date') %>
+ <%= calendar_for('issue_start_date', 'start_date') if @issue.leaf? %>
+ <% end %>
+
+
+
+ <%= l(:field_due_date) %>:
+ <% if @issue.safe_attribute? 'due_date' %>
+ <%= f.text_field :due_date, :size => 22,
+ :disabled => !@issue.leaf?,
+ :no_label => true,
+ :required => @issue.required_attribute?('due_date') %>
+ <%= calendar_for('issue_due_date', 'start_date') if @issue.leaf? %>
+ <% end %>
+
+
+
+ <%= l(:field_estimated_hours) %>:
+ <% if @issue.safe_attribute? 'estimated_hours' %>
+ <%= f.text_field :estimated_hours, :size => 22,
+ :disabled => !@issue.leaf?,
+ :no_label => true,
+ :required => @issue.required_attribute?('estimated_hours') %>
+ <%= l(:field_hours) %>
+ <% end %>
+
+
+ % 完成 :
+ <% if @issue.safe_attribute?('done_ratio') && @issue.leaf? && Issue.use_field_for_done_ratio? %>
+ <%= f.select :done_ratio, ((0..10).to_a.collect { |r| ["#{r*10} %", r*10] }),
+ {:required => @issue.required_attribute?('done_ratio'), :no_label => true},
+ :onchange => "PrecentChange(this.value)",
+ :class => "w150" %>
+ <% end %>
+
+
+
+
+
+
+
+
+
+ <% if @issue.safe_attribute? 'custom_field_values' %>
+ <%= render :partial => 'issues/form_custom_fields' %>
+ <% end %>
<% end %>
diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb
index 8037bee01..1266f4611 100644
--- a/app/views/issues/_edit.html.erb
+++ b/app/views/issues/_edit.html.erb
@@ -1,51 +1,41 @@
<%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %>
<%= error_messages_for 'issue', 'time_entry' %>
<%= render :partial => 'conflict' if @conflict %>
-
+
<% if @edit_allowed || !@allowed_statuses.empty? %>
-
<%= l(:label_change_properties) %>
-
- <%= render :partial => 'form', :locals => {:f => f} %>
+
+ <%= render :partial => 'form', :locals => {:f => f} %>
+
+
+
+
+ <% end %>
+
+
+
+ <% if @journals.present? %>
+
+ <%= render :partial => 'history', :locals => {:issue => @issue, :journals => @journals} %>
-
- <% end %>
-
-
<%= l(:field_notes) %>
- <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %>
- <%= wikitoolbar_for 'issue_notes' %>
-
- <% if @issue.safe_attribute? 'private_notes' %>
- <%= f.check_box :private_notes, :no_label => true %> <%= l(:field_private_notes) %>
<% end %>
-
- <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
+ 回复
+ <%= f.text_area :notes, :style => "width:99%;", :rows => "5", :no_label => true %>
+
+
+
- <%= l(:label_attachment_plural) %>
- <%= render :partial => 'attachments/form', :locals => {:container => @issue} %>
-
+ <%= call_hook(:view_issues_edit_notes_bottom, {:issue => @issue, :notes => @notes, :form => f}) %>
+
-
-
- <%= l(:label_issue_watchers) %>
-
- <%= watchers_checkboxes(@issue, @available_watchers) %>
-
-
- <%= link_to l(:label_search_for_watchers),
- {:controller => 'watchers', :action => 'new', :project_id => @issue.project},
- :remote => true,
- :method => 'get' %>
-
-
-
-
+
+
+
<%= f.hidden_field :lock_version %>
<%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %>
- <%= hidden_field_tag 'reference_user_id', params[:reference_user_id]%>
- <%= submit_tag l(:button_submit) %>
- <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %>
+ <%= hidden_field_tag 'reference_user_id', params[:reference_user_id] %>
<% end %>
diff --git a/app/views/issues/_form.html.erb b/app/views/issues/_form.html.erb
index fb2fa9e55..5679c92d4 100644
--- a/app/views/issues/_form.html.erb
+++ b/app/views/issues/_form.html.erb
@@ -1,65 +1,116 @@
-
<%= labelled_fields_for :issue, @issue do |f| %>
-<%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
+ <%= call_hook(:view_issues_form_details_top, {:issue => @issue, :form => f}) %>
+
+
+
+ <% if @issue.safe_attribute? 'tracker_id' %>
+ * 类型 :
+ <%= f.select :tracker_id,
+ @issue.project.trackers.collect { |t| [t.name, t.id] },
+ {:required => true, :no_label => true},
+ :onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')",
+ :class => "w90"
+ %>
+ <% end %>
+
+
+ <% if @issue.safe_attribute? 'is_private' %>
+ <%= f.check_box :is_private, :no_label => true, :class => "ml30" %>
+ <%= l(:field_is_private) %>
+ <% end %>
+
+
+
+
+ <%#= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), {:required => true},
+ :onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %>
+
+
+
+ <% if @issue.safe_attribute? 'subject' %>
+ * 主题 :
+ <%= f.text_field :subject,
+ :class => "w583",
+ :maxlength => 255,
+ :style => "font-size:small",
+ :no_label => true
+ %>
+
+ <%= javascript_tag do %>
+ observeAutocompleteField('issue_subject',
+ '<%= escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (Setting.cross_project_issue_relations? ? 'all' : nil)) %>
+ ',
+ { select: function(event, ui) {
+ $('input#issue_subject').val(ui.item.value);
+ }
+ });
+ <% end %>
-<% if @issue.safe_attribute? 'is_private' %>
-
- <%= f.check_box :is_private, :no_label => true %><%= l(:field_is_private) %>
-
-<% end %>
+ <% end %>
+
+
-<% if @issue.safe_attribute? 'project_id' %>
-<%= f.select :project_id, project_tree_options_for_select(@issue.allowed_target_projects, :selected => @issue.project), {:required => true},
- :onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %>
-<% end %>
+
+ <% if @issue.safe_attribute? 'description' %>
+ 描述 :
+ <%= f.label_for_field :description, :required => @issue.required_attribute?('description'), :no_label => true, :class => "label" %>
+ <%= link_to_function image_tag('edit.png'), '$(this).hide(); $("#issue_description_and_toolbar").show()' unless @issue.new_record? %>
+ <%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
+ <%= f.text_area :description,
+ :rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
+ :accesskey => accesskey(:edit),
+ :class => "w583",
+ :no_label => true %>
+ <% end %>
-<% if @issue.safe_attribute? 'tracker_id' %>
-<%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, {:required => true},
- :onchange => "updateIssueFrom('#{escape_javascript project_issue_form_path(@project, :id => @issue, :format => 'js')}')" %>
-
-<% end %>
-
-
-<% if @issue.safe_attribute? 'subject' %>
-<%= f.text_field :subject, :size => 80, :maxlength => 255, :required => true, :style => "font-size:small" %>
-
-<%= javascript_tag do %>
- observeAutocompleteField('issue_subject', '<%= escape_javascript auto_complete_issues_path(:project_id => @project,:scope => (Setting.cross_project_issue_relations? ? 'all' : nil)) %>',
- { select: function(event, ui) {
- $('input#issue_subject').val(ui.item.value);
- }
- });
-<% end %>
-
-<% end %>
-
-<% if @issue.safe_attribute? 'description' %>
-
- <%= f.label_for_field :description, :required => @issue.required_attribute?('description') %>
- <%= link_to_function image_tag('edit.png'), '$(this).hide(); $("#issue_description_and_toolbar").show()' unless @issue.new_record? %>
- <%= content_tag 'span', :id => "issue_description_and_toolbar", :style => (@issue.new_record? ? nil : 'display:none') do %>
- <%= f.text_area :description,
- :cols => 60,
- :rows => (@issue.description.blank? ? 10 : [[10, @issue.description.length / 50].max, 100].min),
- :accesskey => accesskey(:edit),
- :class => 'wiki-edit',
- :style => "font-size:small;width:520px;",
- :no_label => true %>
- <% end %>
-
-<%= wikitoolbar_for 'issue_description' %>
-<% end %>
-
-
- <%= render :partial => 'issues/attributes' %>
-
-
-<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
-<% end %>
+ <%= wikitoolbar_for 'issue_description' %>
+ <% end %>
+
+
+
+
+
+ <% if @copy_from && @copy_from.attachments.any? %>
+
+
+
+
+ <% end %>
+ <% if @copy_from && !@copy_from.leaf? %>
+
+ <%= l(:label_copy_subtasks) %>
+ <%= check_box_tag 'copy_subtasks', '1', @copy_subtasks %>
+
+ <% end %>
+
+
+
+
+ <%= l(:label_attachment_plural) %>:
+ <%= render :partial => 'attachments/form', :locals => {:container => @issue} %>
+
+
+
+ <%= render :partial => 'issues/attributes' %>
+
+
+
+
+
+
+ <%#= link_to "",
+# {:controller => 'watchers', :action => 'new', :project_id => @issue.project},
+# :remote => true,
+# :method => 'get',
+ :class => "pic_sch mt5 ml5" %>
+
+ <%#= javascript_tag "observeSearchfield('user_search', 'users_for_watcher', '#{ escape_javascript watchers_autocomplete_for_user_path(:user => @available_watchers, :format => 'js', :flag => 'ture') }')" %>
+
+
+
+
+
+
+ <%= call_hook(:view_issues_form_details_bottom, {:issue => @issue, :form => f}) %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/issues/_history.html.erb b/app/views/issues/_history.html.erb
index 9823e30b7..b0f1ad38d 100644
--- a/app/views/issues/_history.html.erb
+++ b/app/views/issues/_history.html.erb
@@ -1,43 +1,33 @@
<% reply_links = authorize_for('issues', 'edit') -%>
-<% for journal in journals %>
-
+<% journals.reverse.each do |journal| %>
-
-
- <%= image_tag(url_to_avatar(journal.user), :class => "avatar") %>
-
-
-
-
- <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %>
- <%= render_links(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %>
-
-
-
-
-
- <% if journal.details.any? %>
- <% details_to_strings(journal.details).each do |string| %>
-
- <%= string %>
- <% end %>
- <% end %>
- <%= render_notes(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %>
-
-
-
- <%= format_time journal.created_on %>
-
-
-
-
-
+
+
+
+
+
<%= journal.user %> <%= format_time journal.created_on %>
+
+
+ <% if journal.details.any? %>
+ <% details_to_strings(journal.details).each do |string| %>
+
<%= string %>
+ <% end %>
+ <% end %>
+
+
+
+
+
+
<%= render_notes_issue(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %>
+
+
+
+
-
-<%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %>
+ <%= call_hook(:view_issues_history_journal_bottom, { :journal => journal }) %>
<% end %>
<% heads_for_wiki_formatter if User.current.allowed_to?(:edit_issue_notes, issue.project) || User.current.allowed_to?(:edit_own_issue_notes, issue.project) %>
diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb
index 4519b6047..f1b5a319d 100644
--- a/app/views/issues/_list.html.erb
+++ b/app/views/issues/_list.html.erb
@@ -1,69 +1,35 @@
-