diff --git a/.gitignore b/.gitignore index 73b76dc34..ffa4a1bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.swp /config/database.yml /config/configuration.yml +/config/additional_environment.rb /files/* /log/* /public/tmp/* @@ -26,3 +27,4 @@ vendor/cache /files /public/images/avatars /public/files +/tags diff --git a/Gemfile b/Gemfile index 976184793..13b410d9d 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,8 @@ unless RUBY_PLATFORM =~ /w32/ gem 'iconv' end +gem "mysql2", "= 0.3.18" +gem 'redis-rails' gem 'rubyzip' gem 'delayed_job_active_record'#, :group => :production gem 'daemons' @@ -21,9 +23,6 @@ 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' @@ -83,56 +82,13 @@ group :openid do 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` 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/api/mobile/apis/courses.rb b/app/api/mobile/apis/courses.rb index 08c7adf10..0d0c4651f 100644 --- a/app/api/mobile/apis/courses.rb +++ b/app/api/mobile/apis/courses.rb @@ -228,6 +228,32 @@ module Mobile present :status, 0 end + desc '课程课件' + params do + requires :token, type: String + requires :course_id,type: Integer,desc: '课程id' + optional :name,type:String,desc:'课件名称可能包含的字符' + end + get ":course_id/attachments" do + cs = CoursesService.new + count = cs.course_attachments params + present :data, count, with: Mobile::Entities::Attachment + present :status, 0 + end + + desc '课程学生' + params do + requires :token,type:String + requires :course_id,type:Integer,desc: '课程id' + optional :name,type:String,desc:'学生的姓名或者昵称或者学号可能包含的字符' + end + get ":course_id/members" do + cs = CoursesService.new + count = cs.course_members params + present :data, count, with: Mobile::Entities::Member + present :status, 0 + end + end end end diff --git a/app/api/mobile/entities/attachment.rb b/app/api/mobile/entities/attachment.rb index 510db89be..080b24558 100644 --- a/app/api/mobile/entities/attachment.rb +++ b/app/api/mobile/entities/attachment.rb @@ -16,8 +16,11 @@ module Mobile end end end + attachment_expose :id attachment_expose :filename attachment_expose :description + attachment_expose :downloads + attachment_expose :quotes end end end \ No newline at end of file diff --git a/app/api/mobile/entities/course.rb b/app/api/mobile/entities/course.rb index 520f73384..50812b349 100644 --- a/app/api/mobile/entities/course.rb +++ b/app/api/mobile/entities/course.rb @@ -46,6 +46,7 @@ module Mobile course_expose :term course_expose :time course_expose :updated_at + course_expose :course_student_num expose :teacher, using: Mobile::Entities::User do |c, opt| if c.is_a? ::Course c.teacher diff --git a/app/api/mobile/entities/member.rb b/app/api/mobile/entities/member.rb new file mode 100644 index 000000000..837ec788a --- /dev/null +++ b/app/api/mobile/entities/member.rb @@ -0,0 +1,33 @@ +module Mobile + module Entities + class Member < Grape::Entity + include ApplicationHelper + include ApiHelper + def self.member_expose(f) + expose f do |u,opt| + if u.is_a?(Hash) && u.key?(f) + u[f] + elsif u.is_a?(::Member) + if u.respond_to?(f) + u.send(f) + else + case f + when :student_id + u.user.user_extensions.student_id + end + end + end + + end + end + + expose :user, using: Mobile::Entities::User do |c, opt| + if c.is_a?(::Member) + c.user + end + end + member_expose :student_id + member_expose :score + end + end +end \ No newline at end of file diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index f8d0f5dd1..22db3d5b4 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -332,7 +332,7 @@ class AccountController < ApplicationController token = Token.create(:user => user, :action => 'autologin') cookie_options = { :value => token.value, - :expires => 1.year.from_now, + :expires => 7.days.from_now, :path => (Redmine::Configuration['autologin_cookie_path'] || '/'), :secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false), :httponly => true diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 47251cf85..741af2aee 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -894,7 +894,6 @@ class ApplicationController < ActionController::Base set_autologin_cookie(user) end call_hook(:controller_account_success_authentication_after, {:user => user }) - - 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..c98df27d3 100644 --- a/app/controllers/bids_controller.rb +++ b/app/controllers/bids_controller.rb @@ -490,7 +490,7 @@ class BidsController < ApplicationController (SELECT stars FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 1 AND stars IS NOT NULL ORDER BY updated_at DESC limit 0,1) AS t_score, (SELECT AVG(stars) FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 0) AS s_score FROM homework_attaches WHERE bid_id = #{@bid.id} ORDER BY s_score DESC,created_at ASC) AS table1 - WHERE table1.t_score IS NULL OR table1.t_score = 0") + WHERE table1.t_score IS NULL") @not_batch_homework = true @cur_type = 1 else @@ -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/boards_controller.rb b/app/controllers/boards_controller.rb index bdbbb8e37..f6ec09b2a 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -42,12 +42,19 @@ class BoardsController < ApplicationController elsif @course if (User.current.admin? || @course.is_public == 1 || (@course.is_public == 0 && User.current.member_of_course?(@course))) @boards = @course.boards.includes(:last_message => :author).all - @boards = [] << @boards[0] if @boards.any? - if @boards.size == 1 - @board = @boards.first - show and return + if @course.boards.empty? + @board = @course.boards.build + @board.name = " #{l(:label_borad_course) }" + @board.description = @course.name.to_s + @board.project_id = -1 + if @board.save + @boards = @course.boards.includes(:last_message => :author).all + end end - render :layout => 'base_courses' + unless @course.boards.empty? + @board = @course.boards.first + end + show and return else render_403 end @@ -65,7 +72,7 @@ class BoardsController < ApplicationController 'replies' => "#{Message.table_name}.replies_count", 'updated_on' => "COALESCE(last_replies_messages.created_on, #{Message.table_name}.created_on)" - @topic_count = @board.topics.count + @topic_count = @board ? @board.topics.count : 0 if @project @topic_pages = Paginator.new @topic_count, per_page_option, params['page'] @topics = @board.topics. @@ -77,14 +84,13 @@ class BoardsController < ApplicationController preload(:author, {:last_reply => :author}). all elsif @course - board_topics = @board.topics. - reorder("#{Message.table_name}.sticky DESC"). + board_topics = @board ? @board.topics.reorder("#{Message.table_name}.sticky DESC"). includes(:last_reply). # limit(@topic_pages.per_page). # offset(@topic_pages.offset). order(sort_clause). preload(:author, {:last_reply => :author}). - all + all : [] @topics = paginateHelper board_topics,10 end 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..f6b044c63 100644 --- a/app/controllers/homework_attach_controller.rb +++ b/app/controllers/homework_attach_controller.rb @@ -51,7 +51,7 @@ class HomeworkAttachController < ApplicationController order_by = "created_at #{direction}" end all_homework_list = HomeworkAttach.eager_load(:attachments,:user,:rate_averages).find_by_sql("SELECT * FROM (SELECT homework_attaches.*, - (SELECT stars FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 1 AND stars IS NOT NULL AND stars > 0 ORDER BY updated_at DESC limit 0,1) AS t_score, + (SELECT stars FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 1 AND stars IS NOT NULL ORDER BY updated_at DESC limit 0,1) AS t_score, (SELECT AVG(stars) FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 0) AS s_score FROM homework_attaches WHERE bid_id = #{@bid.id} ORDER BY #{order_by}) AS table1 @@ -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 @@ -440,14 +444,16 @@ class HomeworkAttachController < ApplicationController homework = @homework is_teacher = @is_teacher ? 1 : 0 #保存评分@homework.rate(@m_score.to_i,User.current.id,:quality, (@is_teacher ? 1 : 0)) - if @m_score + @is_comprehensive_evaluation = @is_teacher ? 1 : (@is_anonymous_comments ? 2 : 3) #判断当前评论是老师评论?匿评?留言 + if @is_teacher || @is_anonymous_comments + @m_score ||= 0 rate = @homework.rates(:quality).where(:rater_id => User.current.id, :is_teacher_score => is_teacher).first if rate rate.stars = @m_score - rate.save! else - @homework.rates(:quality).new(:stars => @m_score, :rater_id => User.current.id, :is_teacher_score => is_teacher).save! + rate = @homework.rates(:quality).new(:stars => @m_score, :rater_id => User.current.id, :is_teacher_score => is_teacher) end + rate.save! if homework.is_teacher_score == 0 if is_teacher == 1 @@ -465,12 +471,26 @@ class HomeworkAttachController < ApplicationController end end homework.save! - end + end #保存评论 - @is_comprehensive_evaluation = @is_teacher ? 1 : (@is_anonymous_comments ? 2 : 3) #判断当前评论是老师评论?匿评?留言 - if params[:new_form] && params[:new_form][:user_message] && params[:new_form][:user_message] != "" #有没有留言 - @homework.addjours User.current.id, params[:new_form][:user_message],0,@is_comprehensive_evaluation + if params[:new_form] && params[:new_form][:user_message] #有没有留言 + jour = @homework.journals_for_messages.where("is_comprehensive_evaluation = 1 and user_id = #{User.current.id}").order("created_on DESC").first + if params[:new_form][:user_message] == "" + if @is_teacher + unless jour + jour = @homework.addjours User.current.id, "",0,@is_comprehensive_evaluation + end + end + else + jour = @homework.addjours User.current.id, params[:new_form][:user_message],0,@is_comprehensive_evaluation + end + end + + if jour + jour.save_attachments(params[:attachments]) + render_attachment_warning_if_needed(jour) + jour.save end @teacher_stars = @stars_reates.where("is_teacher_score = 1") #老师评分列表 @@ -483,7 +503,7 @@ class HomeworkAttachController < ApplicationController get_not_batch_homework_list params[:cur_sort] || "s_socre",params[:cur_direction] || "desc",@homework.bid_id elsif @cur_type == "2" #老师已批列表 @result_homework = HomeworkAttach.find_by_sql("SELECT homework_attaches.*, - (SELECT stars FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 1 AND stars IS NOT NULL AND stars > 0 ORDER BY updated_at DESC limit 0,1) AS t_score, + (SELECT stars FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 1 AND stars IS NOT NULL ORDER BY updated_at DESC limit 0,1) AS t_score, (SELECT AVG(stars) FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 0) AS s_score FROM homework_attaches WHERE id = #{@homework.id}").first elsif @cur_type == "3" #全部作业列表 @@ -610,9 +630,10 @@ class HomeworkAttachController < ApplicationController (SELECT AVG(stars) FROM seems_rateable_rates WHERE rateable_type = 'HomeworkAttach' AND rateable_id = homework_attaches.id AND is_teacher_score = 0) AS s_score FROM homework_attaches WHERE bid_id = #{bid_id} ORDER BY #{order_by}) AS table1 - WHERE table1.t_score IS NULL OR table1.t_score = 0 ") + WHERE table1.t_score IS NULL ") @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..12ee4bc3b 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -75,7 +75,11 @@ class IssuesController < ApplicationController else @limit = 10#per_page_option end - + @assign_to_id = params[:assigned_to_id] + @author_id = params[:author_id] + @priority_id = params[:priority_id] + @status_id = params[:status_id] + @subject = params[:subject] @issue_count = @query.issue_count @issue_pages = Paginator.new @issue_count, @limit, params['page'] @offset ||= @issue_pages.offset @@ -95,6 +99,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/messages_controller.rb b/app/controllers/messages_controller.rb index 436418430..bab5dfb4c 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -93,7 +93,15 @@ class MessagesController < ApplicationController end call_hook(:controller_messages_new_after_save, { :params => params, :message => @message}) render_attachment_warning_if_needed(@message) - redirect_to board_message_url(@board, @message) + if params[:is_board] + if @project + redirect_to project_boards_path(@project) + elsif @course + redirect_to course_boards_path(@course) + end + else + redirect_to board_message_url(@board, @message) + end else layout_file = @project ? 'base_projects' : 'base_courses' render :action => 'new', :layout => layout_file @@ -131,9 +139,16 @@ class MessagesController < ApplicationController render_attachment_warning_if_needed(@reply) else #render file: 'messages#show', layout: 'base_courses' + end + if params[:is_board] + if @project + redirect_to project_boards_path(@project) + elsif @course + redirect_to course_boards_path(@course) + end + else + redirect_to board_message_url(@board, @topic, :r => @reply) end - redirect_to board_message_url(@board, @topic, :r => @reply) - end # Edit a message @@ -172,16 +187,20 @@ class MessagesController < ApplicationController @message.destroy # modify by nwb if @project - if @message.parent - redirect_to board_message_url(@board, @message.parent, :r => r) - else + if params[:is_board] redirect_to project_boards_url(@project) + else + redirect_to board_message_url(@board, @topic, :r => @reply) end elsif @course - if @message.parent - redirect_to board_message_url(@board, @message.parent, :r => r) + if params[:is_board] + redirect_to course_boards_url(@course) else - redirect_to course_board_url(@course, @board) + if @message.parent + redirect_to board_message_url(@board, @message.parent, :r => r) + else + redirect_to course_board_url(@course, @board) + end end end end diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 7ff015fbc..d42a750a1 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -20,7 +20,7 @@ class MyController < ApplicationController # edit before_filter :auth_login1, :only => [:account] # - before_filter :require_login + before_filter :require_login, except: [:change_mail_notification] helper :issues helper :users @@ -75,6 +75,19 @@ class MyController < ApplicationController end end + def change_mail_notification + token = params[:token] + user = try_to_autologin1 + if user + user.mail_notification = params[:mail_notification] + user.save + flash[:notice] = l(:notice_mail_notification_updated) + redirect_to my_account_url + else + redirect_to signin_url + end + end + # Edit user's account def account @user = User.current diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index d41491349..f71a77ca7 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -152,6 +152,9 @@ class NewsController < ApplicationController end def edit + if @course + render :layout => "base_courses" + end 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..96807d2dc 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 @@ -250,18 +243,13 @@ class ProjectsController < ApplicationController # Author lizanle # Description 项目动态展示方法,删除了不必要的代码 def show - - # 试图跳转到请求的按钮 - if params[:login] - login = params[:login] - login = login.sub(/%40/,'@') - mail = params[:login] - password = params[:password] - us = UsersService.new - user = us.register_auto(login,mail, password) + # params[:login]为邮箱邀请用户加入,主要功能: + # 1、自动注册 + # 2、加入项目、创建角色 + # 3、用户得分 + if params[:email] + user = User.find_by_mail(params[:email].to_s) Member.create(:role_ids => [4], :user_id => user.id,:project_id => @project.id) - UserGrade.create(:user_id => user.id, :project_id => @project.id) - User.current = user unless User.current.nil? end if params[:jump] && redirect_to_project_menu_item(@project, params[:jump]) return @@ -331,14 +319,46 @@ 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 + # 两种情况:1、系统外用户;2、系统内用户 (通过邮件判定) def send_mail_to_member if !params[:mail].blank? && User.find_by_mail(params[:mail].to_s).nil? email = params[:mail] Mailer.run.send_invite_in_project(email, @project, User.current) @is_zhuce =false flash[:notice] = l(:notice_email_sent, :value => email) + elsif !User.find_by_mail(params[:mail].to_s).nil? + user = User.find_by_mail(params[:mail].to_s) + if !user.member_of?(@project) + email = params[:mail] + Mailer.run.request_member_to_project(email, @project, User.current) + flash[:notice] = l(:notice_email_sent, :value => email) + else + flash[:error] = l(:label_member_of_project, :value => email) + end else flash[:error] = l(:notice_registed_error, :value => email) @is_zhuce = true @@ -347,6 +367,7 @@ class ProjectsController < ApplicationController format.html{redirect_to invite_members_by_mail_project_url(@project)} end end + #发送邮件邀请新用户 def invite_members_by_mail if User.current.member_of?(@project) || User.current.admin? @@ -379,7 +400,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 +476,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 +627,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..5fe953563 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 @@ -234,24 +236,10 @@ update end def destroy - @root_path=RepositoriesHelper::ROOT_PATH - @repo_name=User.current.login.to_s+"_"+@repository.identifier.to_s - @repository_name=User.current.login.to_s+"/"+@repository.identifier.to_s+".git" - @middle=User.current.login.to_s+"_"+@repository.identifier.to_s+"-write:" - @repository.destroy if request.delete? + DestroyRepositoryTask.new.destroy(User.current.id, @repository.id) + @repository.hidden = true + @repository.save redirect_to settings_project_url(@project, :tab => 'repositories') - if(@repository.type=="Repository::Git") - logger.info "destory the repository value"+"root path"+@root_path+"repo_name"+@repo_name+ - "repository_name"+@repository_name+"user group"+@middle - system "sed -i /"+@repo_name+"/{d} "+@root_path+"htdocs/user.passwd" - system "sed -i /"+@middle+"/{d} "+@root_path+"htdocs/group.passwd" - system "rm -r "+@root_path+"htdocs/"+@repository_name - # if(@sed_user&&@sed_group&&@remove) - # else - # logger.info "An error occured when destory the repository"+"delete form passwd: \n"+ - # @sed_user+"delete from group"+@sed_group+"delete from file"+@remove - # end - end end def show diff --git a/app/controllers/test_controller.rb b/app/controllers/test_controller.rb index 22cf7d1d5..51ff293f1 100644 --- a/app/controllers/test_controller.rb +++ b/app/controllers/test_controller.rb @@ -57,5 +57,98 @@ class TestController < ApplicationController attach.filename end + def mailer() + raise unless Rails.env.development? + @user = User.find(params[:user_id]) + send_for_user_activities(@user, Time.now,1) + render 'mailer/send_for_user_activities' + end + 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}#{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参加的项目及课程 + 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? + } + #有内容才发,没有不发 + end + -end \ No newline at end of file +end diff --git a/app/controllers/trackers_controller.rb b/app/controllers/trackers_controller.rb index 01bc47a2f..64ad9c83a 100644 --- a/app/controllers/trackers_controller.rb +++ b/app/controllers/trackers_controller.rb @@ -38,12 +38,13 @@ class TrackersController < ApplicationController @tracker ||= Tracker.new(params[:tracker]) @trackers = Tracker.sorted.all @projects = Project.where("project_type = #{Project::ProjectType_project}").all - @courses = Course.all - @course_activity_count=Hash.new - @courses.each do |course| - @course_activity_count[course.id]=0 - end - @course_activity_count=get_course_activity @courses,@course_activity_count + # 去掉原因,这块代码已经没有用到 + # @courses = Course.all + # @course_activity_count=Hash.new + # @courses.each do |course| + # @course_activity_count[course.id]=0 + # end + # @course_activity_count=get_course_activity @courses,@course_activity_count end def create 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/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index f35210f01..6e067fb83 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -58,12 +58,18 @@ class WelcomeController < ApplicationController else case @first_page.sort_type when 0 + @my_projects = find_my_projects + @other_projects = @my_projects.count < 9 ? find_miracle_project( 9 - @my_projects.count, 3,"score desc") : [] @projects = find_miracle_project(10, 3,"created_on desc") #@projects = @projects_all.order("created_on desc") when 1 + @my_projects = find_my_projects + @other_projects = @my_projects.count < 9 ? find_miracle_project( 9 - @my_projects.count, 3,"score desc") : [] @projects = find_miracle_project(10, 3,"score desc") #@projects = @projects_all.order("grade desc") when 2 + @my_projects = find_my_projects + @other_projects = @my_projects.count < 9 ? find_miracle_project( 9 - @my_projects.count, 3,"score desc") : [] @projects = find_miracle_project(10, 3,"watchers_count desc") #@projects = @projects_all.order("watchers_count desc") diff --git a/app/controllers/words_controller.rb b/app/controllers/words_controller.rb index 680a13963..3d6772ea8 100644 --- a/app/controllers/words_controller.rb +++ b/app/controllers/words_controller.rb @@ -81,9 +81,10 @@ class WordsController < ApplicationController @journal_destroyed = JournalsForMessage.delete_message(params[:object_id]) if @journal_destroyed.jour_type == "Bid" @bid = Bid.find(@journal_destroyed.jour_id) - end - if @bid @jours_count = @bid.journals_for_messages.where('m_parent_id IS NULL').count + elsif @journal_destroyed.jour_type == "Course" + @course = Course.find @journal_destroyed.jour_id + @jours_count = @course.journals_for_messages.where('m_parent_id IS NULL').count end respond_to do |format| format.js diff --git a/app/controllers/zipdown_controller.rb b/app/controllers/zipdown_controller.rb index df354f107..06d69f72e 100644 --- a/app/controllers/zipdown_controller.rb +++ b/app/controllers/zipdown_controller.rb @@ -188,6 +188,7 @@ class ZipdownController < ApplicationController files_paths.each do |filename| rename_file = File.basename(filename) rename_file = filename_to_real( File.basename(filename)) if is_attachment + begin zipfile.add(rename_file, filename) rescue Exception => e @@ -238,4 +239,4 @@ class ZipdownController < ApplicationController attach = Attachment.find_by_disk_filename(name) attach.filename end -end \ No newline at end of file +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..405dc72ed 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,23 @@ 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 => "200*200") + imagepath = named_attachment_path(attachment, attachment.filename) + if imagesize + link_to image_tag(thumbnail_path(attachment), height: '73', width: '100', class: 'issue_attachment_picture'), + imagepath, + :title => attachment.filename + + else + link_to image_tag(imagepath , height: '73', width: '100', class: 'issue_attachment_picture'), + imagepath, + :title => attachment.filename + end end # 图片缩略图链接 @@ -310,9 +365,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 +385,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 +401,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 +430,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 +445,7 @@ module ApplicationHelper @project = original_project end s.html_safe - end + end def render_course_nested_lists(courses) s = '' @@ -425,7 +480,7 @@ module ApplicationHelper end - #added by young + #added by young def render_project_nested_lists_new(projects) s = '' if projects.any? @@ -454,7 +509,7 @@ module ApplicationHelper end s.html_safe end - #end + #end def render_page_hierarchy(pages, node=nil, options={}) content = '' if pages[node] @@ -489,14 +544,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 = - ("" + - '').html_safe + ("" + + '').html_safe options << project_tree_options_for_select(projects, :selected => @project) do |p| { :value => project_path(:id => p, :jump => current_menu_item) } @@ -547,6 +610,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 +630,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) }
  • \n" + end + s.html_safe + end + #扩展的checkbox生成 def principals_radio_box_tags_ex(name, principals) s = '' @@ -666,24 +749,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 +841,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 +1005,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 +1091,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 +1211,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 +1332,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 +1350,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 +1417,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 +1452,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 +1478,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 +1490,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 +1508,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 +1539,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 +1561,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 +1767,7 @@ module ApplicationHelper end s end - + def get_memo @new_memo = Memo.new #@new_memo.subject = "有什么想说的,尽管来咆哮吧~~" @@ -1717,6 +1821,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 +1861,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 +1891,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 +1932,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 +1944,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 +1956,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 +1983,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 +1998,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 +2018,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 +2043,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 +2079,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 +2093,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 +2114,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 +2139,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 << " [#{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 << "" - 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 << " [#{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 << "
    " + 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..06b154d36 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -1,96 +1,110 @@ -# 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].lstrip.rstrip != "" + 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].lstrip.rstrip != "" + 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_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..d9d910a66 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -243,26 +243,37 @@ module QueriesHelper # Retrieve query from session or build a new query def retrieve_query - if !params[:query_id].blank? - cond = "project_id IS NULL" - cond << " OR project_id = #{@project.id}" if @project - @query = IssueQuery.find(params[:query_id], :conditions => cond) - raise ::Unauthorized unless @query.visible? - @query.project = @project - session[:query] = {:id => @query.id, :project_id => @query.project_id} - sort_clear - elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil) + # if !params[:query_id].blank? + # cond = "project_id IS NULL" + # cond << " OR project_id = #{@project.id}" if @project + # @query = IssueQuery.find(params[:query_id], :conditions => cond) + # raise ::Unauthorized unless @query.visible? + # @query.project = @project + # session[:query] = {:id => @query.id, :project_id => @query.project_id} + # sort_clear + # elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil) # 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} - else - # retrieve from session - @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id] - @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names]) - @query.project = @project - end + #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] + # @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names]) + # @query.project = @project + # end end def retrieve_query_from_session diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index 3c7123435..773560932 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -18,7 +18,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module RepositoriesHelper - ROOT_PATH="/home/pdl/redmine-2.3.2-0/apache2/" + if Rails.env.development? + ROOT_PATH="/tmp/" if Rails.env.development? + else + ROOT_PATH="/home/pdl/redmine-2.3.2-0/apache2/" + end PROJECT_PATH_CUT = 40 REPO_IP_ADDRESS = Setting.repository_domain 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/helpers/welcome_helper.rb b/app/helpers/welcome_helper.rb index f6e205e82..4800a949a 100644 --- a/app/helpers/welcome_helper.rb +++ b/app/helpers/welcome_helper.rb @@ -443,6 +443,10 @@ module WelcomeHelper resultSet.take(limit) end + def find_my_projects + my_projects = User.current.memberships.all(conditions: "projects.project_type = 0") + end + def sort_project_by_hot_rails project_type=0, order_by='score DESC', limit=15 # Project.find_by_sql(" # SELECT p.id, p.name, p.description, p.identifier, t.project_id 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..239a15881 100644 --- a/app/models/journals_for_message.rb +++ b/app/models/journals_for_message.rb @@ -1,175 +1,179 @@ -# 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 } + acts_as_attachable + + has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy + + validates :notes, presence: true, if: :is_homework_jour? + 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 is_homework_jour? + self.jour_type != "HomeworkAttach" + 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..7756b7e27 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -1,908 +1,931 @@ -# 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) + + login = email + login = login.sub(/%40/,'@') + us = UsersService.new + # 自动激活用户 + user = us.register_auto(login, @email, @password) + + Member.create(:role_ids => [4], :user_id => user.id,:project_id => project.id) + UserGrade.create(:user_id => user.id, :project_id => project.id) + User.current = user unless User.current.nil? + @user = user + @token = Token.get_token_from_user(user, 'autologin') + @project_url = url_for(:controller => 'projects', :action => 'show', :id => project.id,:user => user, :token => @token.value + ) + mail :to => email, :subject => @subject + end + + # 邀请已注册的用户加入项目 + def request_member_to_project(email, project, invitor) + @subject = "#{invitor.name} #{l(:label_invite_project)}: #{project.name} " + user = User.find_by_mail(email.to_s) + @invitor_name = "#{invitor.name}" + @project_name = "#{project.name}" + @user = user + @token = Token.get_token_from_user(user, 'autologin') + @project_url = url_for(:controller => 'projects', :action => 'show', :id => project.id, :email => email, :token => @token.value) + 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}#{l(:label_day_mail)}" + + date_from = "#{date_from} 17:59:59" + date_to = "#{date_to} 17:59:59" + + # 生成token用于直接点击登录 + @user = user + @token = Token.get_token_from_user(user, 'autologin') + + # 查询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? + } + mylogger.debug "Sent activity mail : #{user.mail} - #{has_content}" + #有内容才发,没有不发 + 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) + @user = user + @token = Token.get_token_from_user(user, 'autologin') + @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) + @user = user + @token = Token.get_token_from_user(user, 'autologin') + + + # 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 + + set_language_if_valid @initial_language + m = super headers do |format| + format.text + format.html unless Setting.plain_text_mail? + end + mylogger.debug "Sent a mail from #{m.from} to [#{m.to},#{m.cc}, #{m.bcc if Setting.bcc_recipients?}] subject: #{m.subject}" + m + 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 + if Setting.delayjob_enabled? + Delayed::Worker.logger + else + Rails.logger + 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 + + # 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/principal.rb b/app/models/principal.rb index dfec5b911..0f0746f78 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -19,6 +19,7 @@ class Principal < ActiveRecord::Base self.table_name = "#{table_name_prefix}users#{table_name_suffix}" # Account statuses + # 0 全部;1 活动的; 2 已注册; 3 锁定 STATUS_ANONYMOUS = 0 STATUS_ACTIVE = 1 STATUS_REGISTERED = 2 diff --git a/app/models/project.rb b/app/models/project.rb index 07854822e..f257058ea 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -51,7 +51,7 @@ class Project < ActiveRecord::Base has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" has_many :boards, :dependent => :destroy, :order => "position ASC" has_one :repository, :conditions => ["is_default = ?", true] - has_many :repositories, :dependent => :destroy + has_many :repositories, :dependent => :destroy, conditions: "hidden=false" has_many :changesets, :through => :repository #added by xianbo for delete biding_project has_many :biding_projects, :dependent => :destroy 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/token.rb b/app/models/token.rb index 3131bce8d..c89ff30bc 100644 --- a/app/models/token.rb +++ b/app/models/token.rb @@ -27,6 +27,14 @@ class Token < ActiveRecord::Base self.value = Token.generate_token_value end + def self.get_token_from_user(user, action) + token = Token.where(:action => action, :user_id => user).first + unless token + token = Token.create(user: user, action: action) + end + token + end + # Return true if token has expired def expired? return Time.now > self.created_on + @@validity_time 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/services/courses_service.rb b/app/services/courses_service.rb index e91883bee..22249b953 100644 --- a/app/services/courses_service.rb +++ b/app/services/courses_service.rb @@ -86,7 +86,7 @@ class CoursesService gender = m.user.user_extensions.gender.nil? ? 0 : m.user.user_extensions.gender work_unit = get_user_work_unit m.user location = get_user_location m.user - users << {:id => m.user.id, :img_url => img_url, :nickname => m.user.login, :gender => gender, :work_unit => work_unit, :mail => m.user.mail, :location => location, :brief_introduction => m.user.user_extensions.brief_introduction} + users << {:id => m.user.id, :img_url => img_url, :nickname => m.user.login, :gender => gender, :work_unit => work_unit, :mail => m.user.mail, :location => location, :brief_introduction => m.user.user_extensions.brief_introduction,:realname=>m.user.realname} end users end @@ -169,7 +169,7 @@ class CoursesService unless (course.is_public == 1 || current_user.member_of_course?(course) || current_user.admin?) raise '403' end - {:course => course,:work_unit => work_unit, :img_url => url_to_avatar(course),:current_user_is_member => current_user.member_of_course?(course),:current_user_is_teacher => is_course_teacher(current_user,course)} + {:course => course,:work_unit => work_unit, :img_url => url_to_avatar(course),:current_user_is_member => current_user.member_of_course?(course),:current_user_is_teacher => is_course_teacher(current_user,course),:course_student_num => course ? course.student.count.to_s : 0} end #创建课程 @@ -428,7 +428,53 @@ class CoursesService result end + # 课程课件 + def course_attachments params + result = [] + @course = Course.find(params[:course_id]) + @attachments = @course.attachments.order("created_on desc") + if !params[:name].nil? && params[:name] != "" + @attachments.each do |atta| + result << {:filename => atta.filename, + :description => atta.description, + :downloads => atta.downloads, + :quotes => atta.quotes.nil? ? 0 :atta.quotes } if atta.filename.include?(params[:name]) + + end + else + @attachments.each do |atta| + result << {:filename => atta.filename, + :description => atta.description, + :downloads => atta.downloads, + :quotes => atta.quotes.nil? ? 0 :atta.quotes } + + end + end + result + end + + # 课程学生列表 + def course_members params + @all_members = searchmember_by_name(student_homework_score(0,params[:course_id], 10,"desc"),params[:name]) + end + private + def searchmember_by_name members, name + #searchPeopleByRoles(project, StudentRoles) + mems = [] + if name != "" + name = name.to_s.downcase + members.each do |m| + username = m.user[:lastname].to_s.downcase + m.user[:firstname].to_s.downcase + if(m.user[:login].to_s.downcase.include?(name) || m.user.user_extensions[:student_id].to_s.downcase.include?(name) || username.include?(name)) + mems << m + end + end + else + mems = members + end + mems + end def show_homework_info course,bid,current_user,is_course_teacher author_real_name = bid.author.lastname + bid.author.firstname many_times = course.homeworks.index(bid) + 1 @@ -465,5 +511,52 @@ class CoursesService end + def student_homework_score(groupid,course_id, nums, score_sort_by) + #teachers = find_course_teachers(@course) + #start_from = start_from * nums + sql_select = "" + if groupid == 0 + if nums == 0 + sql_select = "SELECT members.*, SUM(homework_attaches.score) as score FROM members, homework_attaches + WHERE members.course_id = #{course_id} AND members.user_id in (SELECT students_for_courses.student_id FROM students_for_courses WHERE course_id = #{course_id}) AND members.user_id = homework_attaches.user_id + AND homework_attaches.bid_id in (SELECT bid_id FROM homework_for_courses WHERE course_id = #{course_id}) GROUP BY members.user_id + UNION all + SELECT members.*, 0 as score FROM members,homework_attaches,students_for_courses WHERE members.course_id = #{course_id} AND + 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}" + else + sql_select = "SELECT members.*, SUM(homework_attaches.score) as score FROM members, homework_attaches + WHERE members.course_id = #{course_id} AND members.user_id in (SELECT students_for_courses.student_id FROM students_for_courses WHERE course_id = #{course_id}) AND members.user_id = homework_attaches.user_id + AND homework_attaches.bid_id in (SELECT bid_id FROM homework_for_courses WHERE course_id = #{course_id}) GROUP BY members.user_id + UNION all + SELECT members.*, 0 as score FROM members,homework_attaches,students_for_courses WHERE members.course_id = #{course_id} AND + 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}" + + end + else + sql_select = "SELECT members.*, SUM(homework_attaches.score) as score FROM members, homework_attaches + WHERE members.course_id = #{course_id} AND members.user_id in (SELECT students_for_courses.student_id FROM students_for_courses WHERE course_id = #{course_id}) AND members.user_id = homework_attaches.user_id + and members.course_group_id = #{groupid} AND homework_attaches.bid_id in (SELECT bid_id FROM homework_for_courses WHERE course_id = #{course_id}) + GROUP BY members.user_id + UNION all + SELECT members.*, 0 as score FROM members,homework_attaches,students_for_courses WHERE members.course_id = #{course_id} + and members.course_group_id = #{groupid} AND + 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}" + end + sql = ActiveRecord::Base.connection() + homework_scores = Member.find_by_sql(sql_select) + sql.close() + + homework_scores + end + end \ No newline at end of file diff --git a/app/services/users_service.rb b/app/services/users_service.rb index c1f716fd1..3a92bf8f1 100644 --- a/app/services/users_service.rb +++ b/app/services/users_service.rb @@ -44,12 +44,14 @@ class UsersService #location = get_user_location @user #{:id => @user.id, :img_url => img_url, :nickname => @user.login, :gender => gender, :work_unit => work_unit, :mail => @user.mail, :location => location, :brief_introduction => @user.user_extensions.brief_introduction} end + + # 自动注册功能 FOR:邮件邀请 def register_auto(login,mail,password) @user = User.new @user.admin = false @user.register @user.login = login - @user.mail =mail + @user.mail = mail password_confirmation = password should_confirmation_password = true if !password.blank? && !password_confirmation.blank? && should_confirmation_password @@ -59,9 +61,7 @@ class UsersService else @user.password = "" end - @user = automatically_register(@user) - if @user.id != nil ue = @user.user_extensions ||= UserExtensions.new ue.user_id = @user.id @@ -69,6 +69,7 @@ class UsersService end @user end + #显示用户 #id用户id def show_user(params) diff --git a/app/tasks/destroy_repository_task.rb b/app/tasks/destroy_repository_task.rb new file mode 100644 index 000000000..3e434422e --- /dev/null +++ b/app/tasks/destroy_repository_task.rb @@ -0,0 +1,25 @@ +#coding=utf-8 +# + +class DestroyRepositoryTask + def destroy(user_id, rep_id) + user = User.find(user_id) + repository = Repository.find(rep_id) + + Rails.logger.info "start delete repository #{user} #{repository}" + @root_path=RepositoriesHelper::ROOT_PATH + @repo_name=user.login.to_s+"_"+repository.identifier.to_s + @repository_name=user.login.to_s+"/"+repository.identifier.to_s+".git" + @middle=user.login.to_s+"_"+repository.identifier.to_s+"-write:" + repository.destroy + if(repository.type=="Repository::Git") + Rails.logger.info "destory the repository value"+"root path"+@root_path+"repo_name"+@repo_name+ + "repository_name"+@repository_name+"user group"+@middle + system "sed -i /"+@repo_name+"/{d} "+@root_path+"htdocs/user.passwd" + system "sed -i /"+@middle+"/{d} "+@root_path+"htdocs/group.passwd" + system "rm -r "+@root_path+"htdocs/"+@repository_name + end + end + + handle_asynchronously :destroy,:queue => 'repository' +end diff --git a/app/views/account/login.html.erb b/app/views/account/login.html.erb index 4175282b3..bb289eb03 100644 --- a/app/views/account/login.html.erb +++ b/app/views/account/login.html.erb @@ -68,7 +68,7 @@ <% if Setting.autologin? %> <% end %> diff --git a/app/views/attachments/_form.html.erb b/app/views/attachments/_form.html.erb index 18586e809..2e8800617 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,56 @@ <%= 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/_homework_jour_link.html.erb b/app/views/attachments/_homework_jour_link.html.erb new file mode 100644 index 000000000..e0efa211c --- /dev/null +++ b/app/views/attachments/_homework_jour_link.html.erb @@ -0,0 +1,18 @@ +
    + <% for attachment in attachments %> +
    + <%= link_to_short_attachment attachment, :class => 'link_file', :download => true, :length => 100 -%> +
    + <% if attachment.is_text? %> +
    + <%= link_to image_tag('magnifier.png'), + {:controller => 'attachments', + :action => 'show', + :id => attachment, + :filename => attachment.filename}, + :target => "_blank"%> +
    + <% end %> +
    + <% 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..a18d819da --- /dev/null +++ b/app/views/attachments/_project_file_links.html.erb @@ -0,0 +1,99 @@ + + +
    + <% 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..56f1f130e 100644 --- a/app/views/avatar/_avatar_form.html.erb +++ b/app/views/avatar/_avatar_form.html.erb @@ -46,7 +46,7 @@ <%= 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) %> - + <%= file_field_tag 'avatar[image]', @@ -68,6 +68,7 @@ :source_id => source.id.to_s } %> +
    <% content_for :header_tags do %> <%= javascript_include_tag 'avatars' %> diff --git a/app/views/avatar/_avatar_preview.html.erb b/app/views/avatar/_avatar_preview.html.erb new file mode 100644 index 000000000..f7a014c64 --- /dev/null +++ b/app/views/avatar/_avatar_preview.html.erb @@ -0,0 +1,66 @@ + + +
    + +
    +
    + diff --git a/app/views/avatar/_new_avatar_form.html.erb b/app/views/avatar/_new_avatar_form.html.erb index 8ca08c766..e341bc83a 100644 --- a/app/views/avatar/_new_avatar_form.html.erb +++ b/app/views/avatar/_new_avatar_form.html.erb @@ -2,10 +2,10 @@ <%= 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) %> <%= file_field_tag 'avatar[image]', :id => nil, - :class => 'upload_file ', + :class => 'upload_file', :size => "1", :multiple => false, :onchange => 'addInputAvatar(this);', diff --git a/app/views/bids/_alert_anonyoms.html.erb b/app/views/bids/_alert_anonyoms.html.erb index 2f1c443f4..4da6e0a10 100644 --- a/app/views/bids/_alert_anonyoms.html.erb +++ b/app/views/bids/_alert_anonyoms.html.erb @@ -66,10 +66,10 @@

    <% end %>
    - + 确  定 - + 取  消
    diff --git a/app/views/bids/_homework.html.erb b/app/views/bids/_homework.html.erb index a7a703c05..c3a6cc9d0 100644 --- a/app/views/bids/_homework.html.erb +++ b/app/views/bids/_homework.html.erb @@ -1,16 +1,5 @@ <%= render_flash_messages %>
    diff --git a/app/views/bids/_homework_list.html.erb b/app/views/bids/_homework_list.html.erb index c0ae219a5..f11d9d3af 100644 --- a/app/views/bids/_homework_list.html.erb +++ b/app/views/bids/_homework_list.html.erb @@ -57,7 +57,6 @@
    <%= render :partial => 'homework_attach/homeworks_list', :locals => {:homeworks => @homework_list, - :homework_count => @obj_count, :remote => false, :not_batch_homework => @not_batch_homework,:is_student_batch_homework => @is_student_batch_homework}%>
    diff --git a/app/views/bids/_new_homework_form.html.erb b/app/views/bids/_new_homework_form.html.erb index aeb9a5c41..7a5f628f9 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)%>

    -
    -
    -
      -
    • - - -

      -
    • -
    • - - - <% 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 %> -
    • -
      -
    • - - - <%= calendar_for('bid_deadline')%> -
    • -
    • - - > -
      -
    • -
    • - - - <%= l(:label_evaluation_description)%> -

      -
    • -
    • - - <%= 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)%>

    +
    +
    +
      +
    • + + +

      +
    • +
    • + + + <% 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 %> +
    • +
      +
    • + + + <%= calendar_for('bid_deadline')%> +
      +
    • +
    • + + > +
      +
    • +
    • + + + <%= l(:label_evaluation_description)%> +

      +
    • +
    • + + <%= 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/bids/alert_anonymous_comment.js.erb b/app/views/bids/alert_anonymous_comment.js.erb index 5cb4018a6..66c36e223 100644 --- a/app/views/bids/alert_anonymous_comment.js.erb +++ b/app/views/bids/alert_anonymous_comment.js.erb @@ -1,6 +1,6 @@ $('#ajax-modal').html('<%= escape_javascript(render :partial => 'alert_anonyoms', locals: { bid: @bid, totle_size:@totle_size, cur_size:@cur_size, percent:@percent}) %>'); showModal('ajax-modal', '500px'); -$('#ajax-modal').css('height','180px'); +//$('#ajax-modal').css('height','180px'); $('#ajax-modal').siblings().remove(); $('#ajax-modal').before("" + ""); diff --git a/app/views/boards/_course_new.html.erb b/app/views/boards/_course_new.html.erb new file mode 100644 index 000000000..94ba51529 --- /dev/null +++ b/app/views/boards/_course_new.html.erb @@ -0,0 +1,9 @@ +<%= form_for @message, :url =>{:controller=>'messages',:action => 'new', :board_id => @board.id, :is_board => 'true'}, :html => {:multipart => true, :id => 'message-form'} do |f| %> + + <%= render :partial => 'form_course', :locals => {:f => f} %> +
  • + <%= link_to l(:button_cancel), course_boards_path(@course), :class => 'grey_btn fr ml10' %> + <%= l(:button_submit)%> +
    +
  • +<% end %> diff --git a/app/views/boards/_course_show.html.erb b/app/views/boards/_course_show.html.erb index ed2c20448..8f2cddc48 100644 --- a/app/views/boards/_course_show.html.erb +++ b/app/views/boards/_course_show.html.erb @@ -1,28 +1,26 @@ - +
    -

    <%= l(:label_board_plural) %>

    -
    +

    + <% if User.current.language == "zh"%> + <%= h @board.name %> + <% else %> + <%= l(:project_module_boards) %> + <% end %> +

    + <%= l(:label_message_new) %> +
    +
    + +
    +
    +
    +
      + <%= render :partial => 'course_new' %> +
    +
    <% if !User.current.logged?%>
    @@ -31,38 +29,125 @@
    <% end %> -
    -

    - <%= l(:label_totle) %> - <%= @topic_count %> - <%= l(:label_course_momes_count) %> -

    - <%= link_to l(:label_message_new), - new_board_message_path(@board), - :class => 'problem_new_btn fl c_dorange' if User.current.logged? %> -
    -
    + <% if @topics.any? %> <% @topics.each do |topic| %> -
    - <%= 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.truncate(40,ommision:'...')), board_message_path(@board, topic),title: topic.subject.to_s,:class => "problem_tit fl fb c_dblue" %> - <% if topic.sticky? %> - 置顶 +
    + <%= link_to image_tag(url_to_avatar(topic.author), :width=>"42",:height=>"42"), user_path(topic.author),:class =>'talkmain_pic fl' %> +
    + <% author = topic.author.to_s + ":" %> + <%= link_to author, user_path(topic.author), :class =>"talkmain_name fl " %> +

      <%= h(topic.subject) %>

    + <% if topic.sticky? %> + <%= l(:label_board_sticky)%> + <% end %> + + +
    +

    +

    + <%= topic.content %> +
    + + + + +

    + <%= l(:label_activity_time)%>:  <%= format_time topic.created_on %> +
    + <%= toggle_link l(:button_reply), "reply" + topic.id.to_s, :focus => 'message_content',:class => ' c_dblue fr' %> + +
    +
    +
    + <% reply = Message.new(:subject => "RE: #{@message.subject}")%> + <% if !topic.locked? && authorize_for('messages', 'reply') %> + +
    + <% end %> -
    -

    由<%= link_to topic.author,user_path(topic.author),:class => "problem_name" %>添加于<%= format_time(topic.created_on) %>

    -
    - <%=link_to (l(:label_reply) + topic.replies_count.to_s), board_message_path(@board, topic),:class => "talk_btn fr c_white" %> + <% replies_all = topic.children. + includes(:author, :attachments, {:board => :project}). + reorder("#{Message.table_name}.created_on DESC").offset(2). + all %> + <% replies_show = topic.children. + includes(:author, :attachments, {:board => :project}). + reorder("#{Message.table_name}.created_on DESC").limit(2). + all %> + <% unless replies_show.empty? %> + <% reply_count = 0 %> +
    +
      + <% replies_show.each do |message| %> + +
    • + <%= link_to image_tag(url_to_avatar(message.author), :width => '34',:height => '34'), user_path(message.author), :class =>'Msg_pic' %> +
      + <%= link_to_user_header message.author,false,:class => 'fl c_orange ' %> +
      +

      <%= textAreailizable message,:content,:attachments => message.attachments %>

      + +
      + <%= format_time(message.created_on) %> + <%= link_to( + + l(:button_delete), + {:controller => 'messages', :action => 'destroy', :id => message.id, :board_id => message.board_id, :is_board => 'true'}, + :method => :post, + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:button_delete), + :class => ' c_dblue fr' + ) if message.course_destroyable_by?(User.current) %> +
      +
      + +
    • + <% end %> +
    +
    + +
  • + <%= link_to image_tag(url_to_avatar(message.author), :width => '34',:height => '34'), user_path(message.author), :class =>'Msg_pic' %> +
    + <%= link_to_user_header message.author,false,:class => 'fl c_orange ' %> +
    +

    <%= textAreailizable message,:content,:attachments => message.attachments %>

    + +
    + <%= format_time(message.created_on) %> + <%= link_to( + + l(:button_delete), + {:controller => 'messages', :action => 'destroy', :id => message.id, :board_id => message.board_id, :is_board => 'true'}, + :method => :post, + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:button_delete), + :class => ' c_dblue fr' + ) if message.course_destroyable_by?(User.current) %> +
    +
    + +
  • + <% end %> + +
    + + + <% end %> +
    <% end %> <% else %> -

    - <%= l(:label_no_data) %> -

    +

    <%= l(:label_no_data) %>

    <% 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/_form_course.html.erb b/app/views/boards/_form_course.html.erb new file mode 100644 index 000000000..5cd2d8a2f --- /dev/null +++ b/app/views/boards/_form_course.html.erb @@ -0,0 +1,60 @@ +<%= error_messages_for 'message' %> +<% replying ||= false %> +<% extra_option = replying ? { readonly: true} : { maxlength: 200 } %> +<% if replying %> +
  • + + + <%= f.text_field :subject, { size: 60, id: "message_subject",:class=>"talk_input w585" }.merge(extra_option) %> + + +

    +
  • +<% else %> +
  • + + + + <%= f.text_field :subject, { size: 60, id: "message_subject", onkeyup: "regexSubject();",:class=>"talk_input w585" }.merge(extra_option) %> + +

    +
  • +<% end %> +
  • + <% unless replying %> + <% if @message.safe_attribute? 'sticky' %> + <%= f.check_box :sticky %> + <%= label_tag 'message_sticky', l(:label_board_sticky) %> + <% end %> + <% if @message.safe_attribute? 'locked' %> + <%= f.check_box :locked %> + <%= label_tag 'message_locked', l(:label_board_locked) %> + <% end %> + <% end %> +
    +
  • +
  • +
    + <% unless replying %> + + <% end %> + <%= text_area :quote,:quote,:style => 'display:none' %> + <% if replying%> + <%= f.text_area :content, :class => 'talk_text fl', :id => 'message_content', :onkeyup => "regexContent();", :maxlength => 5000,:placeholder => "最多3000个汉字(或6000个英文字符)", :style=>"width: 575px;" %> + <% else %> + <%= f.text_area :content, :class => 'talk_text fl', :id => 'message_content', :onkeyup => "regexContent();", :maxlength => 5000,:placeholder => "最多3000个汉字(或6000个英文字符)" %> + <% end %> +
    +

    +
  • +
    +
  • + <% unless replying %> +
    + <%= render :partial => 'attachments/form_course', :locals => {:container => @message,:isReply => @isReply} %> +
    + <% end %> +
  • +
  • +
    +
  • \ No newline at end of file diff --git a/app/views/boards/_form_project.html.erb b/app/views/boards/_form_project.html.erb new file mode 100644 index 000000000..b42cabbeb --- /dev/null +++ b/app/views/boards/_form_project.html.erb @@ -0,0 +1,60 @@ +<%= error_messages_for 'message' %> +<% replying ||= false %> +<% extra_option = replying ? { readonly: true} : { maxlength: 200 } %> +<% if replying %> +
  • + + + <%= f.text_field :subject, { size: 60, id: "message_subject",:class=>"talk_input w585" }.merge(extra_option) %> + + +

    +
  • +<% else %> +
  • + + + + <%= f.text_field :subject, { size: 60, id: "message_subject", onkeyup: "regexSubject();",:class=>"talk_input w585" }.merge(extra_option) %> + +

    +
  • +<% end %> +
  • + <% unless replying %> + <% if @message.safe_attribute? 'sticky' %> + <%= f.check_box :sticky %> + <%= label_tag 'message_sticky', l(:label_board_sticky) %> + <% end %> + <% if @message.safe_attribute? 'locked' %> + <%= f.check_box :locked %> + <%= label_tag 'message_locked', l(:label_board_locked) %> + <% end %> + <% end %> +
    +
  • +
  • +
    + <% unless replying %> + + <% end %> + <%= text_area :quote,:quote,:style => 'display:none' %> + <% if replying%> + <%= f.text_area :content, :class => 'talk_text fl', :id => 'message_content', :onkeyup => "regexContent();", :maxlength => 5000,:placeholder => "最多3000个汉字(或6000个英文字符)", :style=>"width: 575px;" %> + <% else %> + <%= f.text_area :content, :class => 'talk_text fl', :id => 'message_content', :onkeyup => "regexContent();", :maxlength => 5000,:placeholder => "最多3000个汉字(或6000个英文字符)" %> + <% end %> +
    +

    +
  • +
    +
  • +<% unless replying %> +
    + <%= render :partial => 'attachments/form_project', :locals => {:container => @message,:isReply => @isReply} %> +
    + <% end %> +
  • +
  • +
    +
  • \ No newline at end of file diff --git a/app/views/boards/_project_new_topic.html.erb b/app/views/boards/_project_new_topic.html.erb new file mode 100644 index 000000000..371007a72 --- /dev/null +++ b/app/views/boards/_project_new_topic.html.erb @@ -0,0 +1,9 @@ +<%= form_for @message, :url =>{:controller=>'messages',:action => 'new', :board_id => @board.id, :is_board => 'true'}, :html => {:multipart => true, :id => 'message-form'} do |f| %> + + <%= render :partial => 'form_project', :locals => {:f => f} %> +
  • + <%= link_to l(:button_cancel), project_boards_path(@project), :class => 'grey_btn fr ml10' %> + <%= l(:button_submit)%> +
    +
  • +<% end %> \ 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..ef2591b30 100644 --- a/app/views/boards/_project_show.html.erb +++ b/app/views/boards/_project_show.html.erb @@ -1,100 +1,198 @@ - -
    - <% 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(:button_submit)%> - <%= link_to l(:button_cancel), "#", :onclick => '$("#add-message").hide(); return false;', :class => 'ButtonColor m3p10' %> -

    - <% end %> -
    - <% end %> +
    +

    + <% if User.current.language == "zh"%> + <%= h @board.name %> + <% else %> + <%= l(:project_module_boards) %> + <% end %> + +

    + <%= l(:label_message_new) %> +
    <% 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) %> - <%= 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 %> -
    -
    + +
    +
    +
    +
      + <%= render :partial => 'project_new_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 h(topic.subject), board_message_path(@board, topic), title:topic.subject.to_s, :class =>"problem_tit fl" %> + +<% if @topics.any? %> + <% @topics.each do |topic| %> +
    + <%= link_to image_tag(url_to_avatar(topic.author), :width=>"42",:height=>"42"), user_path(topic.author),:class =>'talkmain_pic fl' %> +
    + <% author = topic.author.to_s + ":" %> + <%= link_to author, user_path(topic.author), :class =>"talkmain_name fl " %> +

      <%= h(topic.subject) %>

    <% 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 %> -
    + +
    + +

    + <%= topic.content %> +

    + + + + + +

    + + + + <%= l(:label_activity_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;" %> + <%= toggle_link l(:button_reply), "reply" + topic.id.to_s, :focus => 'message_content',:class => ' c_dblue fr' %> +
    - <% end %> -
      - <%= pagination_links_full @topic_pages, @topic_count, :per_page_links => false, :remote => false, :flag => true %> -
    +
    + <% reply = Message.new(:subject => "RE: #{@message.subject}")%> + <% if !topic.locked? && authorize_for('messages', 'reply') %> + +
    + + <% end %> + <% replies_all = topic.children. + includes(:author, :attachments, {:board => :project}). + reorder("#{Message.table_name}.created_on DESC").offset(2). + all %> + <% replies_show = topic.children. + includes(:author, :attachments, {:board => :project}). + reorder("#{Message.table_name}.created_on DESC").limit(2). + all %> + <% unless replies_show.empty? %> + <% reply_count = 0 %> +
    +
      + <% replies_show.each do |message| %> + +
    • + <%= link_to image_tag(url_to_avatar(message.author), :width => '34',:height => '34'), user_path(message.author), :class =>'Msg_pic' %> +
      + <%= link_to_user_header message.author,false,:class => 'fl c_orange ' %> +
      +

      <%= textAreailizable message,:content,:attachments => message.attachments %>

      + +
      + <%= format_time(message.created_on) %> + <%= link_to( + + l(:button_delete), + {:controller => 'messages', :action => 'destroy', :id => message.id, :board_id => message.board_id, :is_board => 'true'}, + :method => :post, + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:button_delete), + :class => ' c_dblue fr' + ) if message.course_destroyable_by?(User.current) %> +
      +
      + +
    • + <% end %> +
    +
    + + + + <% end %> +
    + <% end %> <% 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 %> <% content_for :header_tags do %> <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@project}: #{@board}") %> <% end %> +
    + \ No newline at end of file diff --git a/app/views/boards/show.html.erb b/app/views/boards/show.html.erb index 81d1288fd..3b810a4b2 100644 --- a/app/views/boards/show.html.erb +++ b/app/views/boards/show.html.erb @@ -1,6 +1,109 @@ - -<% 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/contests/_form_contest.html.erb b/app/views/contests/_form_contest.html.erb index ec993fae8..344d69571 100644 --- a/app/views/contests/_form_contest.html.erb +++ b/app/views/contests/_form_contest.html.erb @@ -67,7 +67,7 @@ <%= f.text_field :deadline, :required => true, :size => 60, - :style => "width:150px;", + :style => "width:150px;float:left;", :readonly => true, :placeholder => "#{l(:label_deadline)}" %> diff --git a/app/views/courses/_course.html.erb b/app/views/courses/_course.html.erb index 8974c3089..755dd63b0 100644 --- a/app/views/courses/_course.html.erb +++ b/app/views/courses/_course.html.erb @@ -58,14 +58,14 @@ <%= content_tag('span', "#{garble @course.members.count}", :class => "info") %> <%= content_tag('span', l(:label_x_member, :count => memberCount(@course))) %>

    - +

    <%= content_tag('span', link_to("#{@course_activity_count[@course.id]}", course_path(@course)), :class => "info") %> <%= content_tag('span', l(:label_x_activity, :count => @course_activity_count[@course.id])) %>

    - +
    <% if(course_endTime_timeout? @course) %> @@ -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 %> -
    -
    <%= image_tag( "/images/sidebar/tags.png") %> diff --git a/app/views/courses/_course_members.html.erb b/app/views/courses/_course_members.html.erb index c093d4b2f..78dc6c319 100644 --- a/app/views/courses/_course_members.html.erb +++ b/app/views/courses/_course_members.html.erb @@ -1,50 +1,50 @@ - -
    -
      -
    • - - 用户 - - - 角色 - - -
    • - -
      - <%= render :partial => "courses/member" %> -
      -
    -
    -
    -

    添加成员

    - <%= form_for(@member, {:as => :membership, :url => course_memberships_path(@course), :remote => true, :method => :post}) do |f| %> - - <% end%> + +
    +
      +
    • + + 用户 + + + 角色 + + +
    • + +
      + <%= render :partial => "courses/member" %> +
      +
    +
    +
    +

    添加成员

    + <%= form_for(@member, {:as => :membership, :url => course_memberships_path(@course), :remote => true, :method => :post}) do |f| %> + + <% end%>
    \ No newline at end of file diff --git a/app/views/courses/_course_teacher.html.erb b/app/views/courses/_course_teacher.html.erb index 587d9d2da..5571a4546 100644 --- a/app/views/courses/_course_teacher.html.erb +++ b/app/views/courses/_course_teacher.html.erb @@ -16,7 +16,7 @@ <% end%>
      - <%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%> + <%#= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%>
    diff --git a/app/views/courses/_courses_jours.html.erb b/app/views/courses/_courses_jours.html.erb index ef1d25bd4..adbbf4780 100644 --- a/app/views/courses/_courses_jours.html.erb +++ b/app/views/courses/_courses_jours.html.erb @@ -1,31 +1,31 @@ -<%= javascript_include_tag "/assets/kindeditor/kindeditor" %> -
    - <%# reply_allow = JournalsForMessage.create_by_user? User.current %> -

    <%= l(:label_leave_message) %>

    - - <% if !User.current.logged?%> -
    - <%= l(:label_user_login_tips) %> - <%= link_to l(:label_user_login_new), signin_path %> -
    -
    - <% else %> - <%= form_for('new_form', :method => :post, - :url => {:controller => 'words', :action => 'leave_course_message'},:html => {:id=>'leave_message_form'}) do |f|%> - <%= hidden_field_tag :asset_id,params[:asset_id],:required => false,:style => 'display:none' %> - <%= f.kindeditor 'course_message',:height => '140px;',:editor_id => 'leave_message_editor',:input_html=>{:id => "leave_meassge",:style => "resize: none;", - :placeholder => "#{l(:label_welcome_my_respond)}",:maxlength => 250}%> - 取  消 - - <%= l(:button_leave_meassge)%> - - <% end %> - <% end %> -
    - -
    - <%= render :partial => 'history',:locals => { :contest => @contest, :journals => @jour, :state => false} %> -
    -
      - <%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%> +<%= javascript_include_tag "/assets/kindeditor/kindeditor" %> +
      + <%# reply_allow = JournalsForMessage.create_by_user? User.current %> +

      <%= l(:label_leave_message) %>

      + + <% if !User.current.logged?%> +
      + <%= l(:label_user_login_tips) %> + <%= link_to l(:label_user_login_new), signin_path %> +
      +
      + <% else %> + <%= form_for('new_form', :method => :post, + :url => {:controller => 'words', :action => 'leave_course_message'},:html => {:id=>'leave_message_form'}) do |f|%> + <%= hidden_field_tag :asset_id,params[:asset_id],:required => false,:style => 'display:none' %> + <%= f.kindeditor 'course_message',:height => '140px;',:editor_id => 'leave_message_editor',:input_html=>{:id => "leave_meassge",:style => "resize: none;", + :placeholder => "#{l(:label_welcome_my_respond)}",:maxlength => 250}%> + 取  消 + + <%= l(:button_leave_meassge)%> + + <% end %> + <% end %> +
      + +
      + <%= render :partial => 'history',:locals => { :contest => @contest, :journals => @jour, :state => false} %> +
      +
        + <%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%>
      \ No newline at end of file diff --git a/app/views/courses/_new_member_list.html.erb b/app/views/courses/_new_member_list.html.erb index 30b77ee50..e8eebe00d 100644 --- a/app/views/courses/_new_member_list.html.erb +++ b/app/views/courses/_new_member_list.html.erb @@ -70,9 +70,9 @@ <% end; reset_cycle %>
    -
      - <%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => @is_remote, :flag => true%> -
    + <% else%>

    <%= l(:label_no_data) %> diff --git a/app/views/courses/_searchmembers.html.erb b/app/views/courses/_searchmembers.html.erb index c6b577c8d..1956f34c4 100644 --- a/app/views/courses/_searchmembers.html.erb +++ b/app/views/courses/_searchmembers.html.erb @@ -1,5 +1,5 @@ <%= form_tag( searchmembers_course_path(@course), method: 'get',:class => "f_l",:remote=>true,:id => "search_student") do %> - <%= text_field_tag 'name', params[:name], name: "name", :class => 'st_search_input', :placeholder => '输入学生姓名、学号进行搜索'%> + <%= text_field_tag 'name', params[:name], name: "name", :class => 'st_search_input', :placeholder => '输入学生昵称、姓名、学号进行搜索'%> <% if @group %> <%= hidden_field "search_group_id", params[:search_group_id],:value => "#{@group.id}", name: 'search_group_id' %> diff --git a/app/views/courses/_show_member_score.html.erb b/app/views/courses/_show_member_score.html.erb index 0f962da81..bb080b57b 100644 --- a/app/views/courses/_show_member_score.html.erb +++ b/app/views/courses/_show_member_score.html.erb @@ -27,15 +27,15 @@ /***弹框***/ - #popbox_tscore{width:480px;position:absolute;z-index:100;left:50%;top:50%;margin:-215px 0 0 -300px; background:#fff; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; box-shadow:0px 0px 8px #194a81; overflow:auto;} - .alert .close02{width:26px;height:26px;overflow:hidden;position:absolute;top:-10px;right:-490px;background:url(images/close.png) no-repeat;cursor:pointer;} - .tscore_con h2{ display:block; background:#eaeaea; font-size:14px; color:#343333; height:31px; width: auto; margin-top:25px; padding-left:20px; padding-top:5px;} + /*#popbox_tscore{width:480px;position:absolute;z-index:100;left:50%;top:50%;margin:-215px 0 0 -300px; background:#fff; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; box-shadow:0px 0px 8px #194a81; overflow:auto;}*/ + /*.alert .close02{width:26px;height:26px;overflow:hidden;position:absolute;top:-10px;right:-490px;background:url(images/close.png) no-repeat;cursor:pointer;}*/ + .tscore_con h2{ display:block; background:#eaeaea; font-size:14px; color:#343333; height:31px; width: auto; text-align: center; padding-top:5px;} .tscore_box{ width:350px; margin:15px auto;} .tscore_box li{ height:25px;} -

    +

    <%= @member_score.user.name %> 历次作业积分

    • 作业名称得分
    • diff --git a/app/views/courses/new_homework.html.erb b/app/views/courses/new_homework.html.erb index ac26c2f29..9ec73d519 100644 --- a/app/views/courses/new_homework.html.erb +++ b/app/views/courses/new_homework.html.erb @@ -1,4 +1,4 @@ -<%= javascript_include_tag "/assets/kindeditor/kindeditor" %> -<%= labelled_form_for @homework, :html => { :multipart => true }, :url => {:controller => 'bids', :action => 'create_homework',:course_id => "#{params[:id] || params[:course_id]}"} do |f| %> - <%= render :partial => 'bids/new_homework_form', :locals => { :bid => @homework,:bid_id => "new_bid",:f => f,:edit_mode => false } %> +<%= javascript_include_tag "/assets/kindeditor/kindeditor" %> +<%= labelled_form_for @homework, :html => { :multipart => true }, :url => {:controller => 'bids', :action => 'create_homework',:course_id => "#{params[:id] || params[:course_id]}"} do |f| %> + <%= render :partial => 'bids/new_homework_form', :locals => { :bid => @homework,:bid_id => "new_bid",:f => f,:edit_mode => false } %> <% end %> \ No newline at end of file diff --git a/app/views/courses/settings.html.erb b/app/views/courses/settings.html.erb index e68be7141..69ce4f5dc 100644 --- a/app/views/courses/settings.html.erb +++ b/app/views/courses/settings.html.erb @@ -1,92 +1,92 @@ -
      -

      <%= l(:label_course_modify_settings)%>

      -
      -
      -
      -
        -
      • - 基本信息 -
      • -
      • - 成员 -
      • -
      -
      -
      -
        - <%= labelled_form_for @course do |f| %> -
      • - <%= render :partial => "avatar/new_avatar_form", :locals => {source: @course} %> -
        -
      • -
      • - - - -
      • -
        -
      • - - - -
      • -
        -
      • - - <%= select_tag :time,options_for_select(course_time_option(@course.time),@course.time), {} %> - <%= select_tag :term,options_for_select(course_term_option,@course.term || cur_course_term),{} %> -
      • -
        -
      • - - - 显示明码 - -
        - 学生或其他成员申请加入课程时候需要使用该口令,该口令可以由老师在课堂上公布。 -
      • -
      • - - -
        -
      • -
      • - - id="course_is_public" name="course[is_public]" type="checkbox"> - (打钩为公开,不打钩则不公开,若不公开,仅课程成员可见该课程。) -
        -
      • -
      • - - id="course_open_student" name="course[open_student]" type="checkbox" style="margin-left: 1px;"/> - (打钩为"学生列表公开",不打钩为不公开,若不公开,则课程外部人员看不到学生列表) -
        -
      • -
      • - 提交 - <%= link_to l(:button_cancel), course_path(@course), :class => "blue_btn grey_btn fl c_white" %> -
        -
      • - <% end %> -
      -
      - -
      -
      - <%= form_tag({:controller => 'courses', :action => 'search_member'},:id => "course_member_search_form", :method => :get, :class => "search_form_course",:remote => true) do %> - - <%= text_field_tag 'name', params[:name], :placeholder => "昵称、学号、姓名搜索", :class => "search_text fl" %> - - <%= l(:label_search)%> - -
      - - <% end %> -
      -
      -
      - <%= render :partial => "course_members" %> -
      -
      -
      +
      +

      <%= l(:label_course_modify_settings)%>

      +
      +
      +
      +
        +
      • + 基本信息 +
      • +
      • + 成员 +
      • +
      +
      +
      +
        + <%= labelled_form_for @course do |f| %> +
      • + <%= render :partial => "avatar/new_avatar_form", :locals => {source: @course} %> +
        +
      • +
      • + + + +
      • +
        +
      • + + + +
      • +
        +
      • + + <%= select_tag :time,options_for_select(course_time_option(@course.time),@course.time), {} %> + <%= select_tag :term,options_for_select(course_term_option,@course.term || cur_course_term),{} %> +
      • +
        +
      • + + + 显示明码 + +
        + 学生或其他成员申请加入课程时候需要使用该口令,该口令可以由老师在课堂上公布。 +
      • +
      • + + +
        +
      • +
      • + + id="course_is_public" name="course[is_public]" type="checkbox"> + (打钩为公开,不打钩则不公开,若不公开,仅课程成员可见该课程。) +
        +
      • +
      • + + id="course_open_student" name="course[open_student]" type="checkbox" style="margin-left: 1px;"/> + (打钩为"学生列表公开",不打钩为不公开,若不公开,则课程外部人员看不到学生列表) +
        +
      • +
      • + 提交 + <%= link_to l(:button_cancel), course_path(@course), :class => "blue_btn grey_btn fl c_white" %> +
        +
      • + <% end %> +
      +
      + +
      +
      + <%= form_tag(search_member_course_path,:id => "course_member_search_form", :method => :get, :class => "search_form_course",:remote => true) do %> + + <%= text_field_tag 'name', params[:name], :placeholder => "昵称、学号、姓名搜索", :class => "search_text fl" %> + + <%= l(:label_search)%> + +
      + + <% end %> +
      + +
      + <%= render :partial => "course_members" %> +
      +
      +
      \ No newline at end of file diff --git a/app/views/courses/show_member_score.js.erb b/app/views/courses/show_member_score.js.erb index fe8d52a2b..87e728901 100644 --- a/app/views/courses/show_member_score.js.erb +++ b/app/views/courses/show_member_score.js.erb @@ -1,3 +1,13 @@ +//$('#ajax-modal').html('<%#= escape_javascript(render :partial => 'courses/show_member_score', :locals => {:member => @member_score}) %>'); +//showModal('ajax-modal', '400px'); +//$('#ajax-modal').addClass('new-watcher'); + $('#ajax-modal').html('<%= escape_javascript(render :partial => 'courses/show_member_score', :locals => {:member => @member_score}) %>'); showModal('ajax-modal', '400px'); -$('#ajax-modal').addClass('new-watcher'); +//$('#ajax-modal').css('height','569px'); +$('#ajax-modal').siblings().remove(); +$('#ajax-modal').before("" + +""); +//$('#ajax-modal').parent().removeClass(); +$('#ajax-modal').parent().css("top","30%").css("left","40%").css("position","fixed"); +$('#ajax-modal').parent().addClass("new-watcher"); diff --git a/app/views/files/_course_file.html.erb b/app/views/files/_course_file.html.erb index 7b2acd875..1769b1a45 100644 --- a/app/views/files/_course_file.html.erb +++ b/app/views/files/_course_file.html.erb @@ -62,11 +62,11 @@

      上传: - 课件 |  - 软件 |  - 媒体 |  - 代码 |  - 其他 + 课件 |  + 软件 |  + 媒体 |  + 代码 |  + 其他

      <% end %>
    diff --git a/app/views/files/_project_file_list.html.erb b/app/views/files/_project_file_list.html.erb index c7e0cec29..3dd5134d7 100644 --- a/app/views/files/_project_file_list.html.erb +++ b/app/views/files/_project_file_list.html.erb @@ -24,9 +24,9 @@ <%= link_to(l(:label_slected_to_other_project),quote_resource_show_project_project_file_path(project,file),:class => "f_l re_select",:remote => true) if has_project?(User.current,file) %> <% if manage_allowed && file.container_id == project.id && file.container_type == "Project" %> - - <%= link_to (file.is_public? ? "公开":"私有"), update_file_dense_attachments_path(:attachmentid=>file.id,:newtype=>(file.is_public? ? 0:1)),:remote=>true,:class=>"f_l re_open",:method => :post %> - + + <%= link_to (file.is_public? ? "公开":"私有"), update_file_dense_attachments_path(:attachmentid=>file.id,:newtype=>(file.is_public? ? 0:1)),:remote=>true,:class=>"f_l re_open",:method => :post %> + <% else %> <% end %> @@ -45,8 +45,8 @@
    - <%= render :partial => 'tags/tag_new', :locals => {:obj => file, :object_flag => "10"} %> - <%= render :partial => 'tags/tag_add', :locals => {:obj => file, :object_flag => "10"} %> + <%= render :partial => 'tags/tag_new', :locals => {:obj => file, :object_flag => "6"} %> + <%= render :partial => 'tags/tag_add', :locals => {:obj => file, :object_flag => "6"} %>
    diff --git a/app/views/files/_project_file_new.html.erb b/app/views/files/_project_file_new.html.erb index 89088a295..1e217f991 100644 --- a/app/views/files/_project_file_new.html.erb +++ b/app/views/files/_project_file_new.html.erb @@ -1,8 +1,3 @@ - -
    -

    <%= l(:lable_file_sharingarea) %>

    -
    - - <% 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')}, - {:onchange => "PrecentChange(this.value)"} %>

    - <% end %> - -
    -
    - - -<% if @issue.safe_attribute? 'custom_field_values' %> -<%= render :partial => 'issues/form_custom_fields' %> -<% end %> +
    +
    + <%= l(:label_change_properties) %> +
      +
    • + + <% 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 %> +
    • +
      +
    • + + <% 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 %> +
    • +
      +
    • +
    • + + <% 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? %> + + <%= 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 %> +
    • +
      + +
    +
      +
    • + + <% 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 %> +
    • +
      +
    • + + <% 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 %> +
    • +
      +
    • + + <% 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..e4c1290aa 100644 --- a/app/views/issues/_edit.html.erb +++ b/app/views/issues/_edit.html.erb @@ -1,51 +1,43 @@ <%= 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} %> + -
    - <% end %> -
    <%= l(:field_notes) %> - <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> - <%= wikitoolbar_for 'issue_notes' %> + <% end %> - <% if @issue.safe_attribute? 'private_notes' %> - + + + <% if @journals.present? %> +
    + <%= render :partial => 'history', :locals => {:issue => @issue, :journals => @journals} %> +
    <% 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}) %> + -
    -

    - - - <%= 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..4cd370adc 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 @copy_from && @copy_from.attachments.any? %> +

    + + +

    + <% end %> + <% if @copy_from && !@copy_from.leaf? %> +

    + + <%= check_box_tag 'copy_subtasks', '1', @copy_subtasks %> +

    + <% end %> +
  • +
    +
  • +
    + + <%= 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 %> 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_links_easy(issue, journal, :reply_links => reply_links) unless journal.notes.blank? %>
    + +

    <%= 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 @@ -
    - -
    \ No newline at end of file + <% end -%> + \ No newline at end of file diff --git a/app/views/issues/index.html.erb b/app/views/issues/index.html.erb index b75a90c55..5307b3bf6 100644 --- a/app/views/issues/index.html.erb +++ b/app/views/issues/index.html.erb @@ -1,18 +1,77 @@ +

    <%= l(:label_issue_tracking) %>

    <% unless @project.enabled_modules.where("name = 'issue_tracking'").empty? %> - - <% if User.current.member_of?(@project) %> - <%= link_to l(:label_issue_new), {:controller => 'issues', :action => 'new', :copy_from => nil}, :param => :project_id, :caption => :label_issue_new, - :html => {:accesskey => Redmine::AccessKeys.key_for(:new_issue)}, :class => 'icon icon-add' %> - <% end %> - <%= link_to l(:label_query), '#', :class => 'icon icon-help', - :onclick => '$("#custom_query").slideToggle(400); ' if true || User.current.logged? %> - + <%#= form_tag({:controller => 'issues', :action => 'index', :project_id => @project}, :method => :get,:id=>"issue_query_form", :class => 'query_form') do %> + <%= hidden_field_tag 'set_filter', '1' %> + + +
    + <%= select( :issue,:user_id, @project.members.order("lower(users.login)").map{|c| [c.name, c.user_id]}.unshift(["指派给",0]), + { :include_blank => false,:selected=>@assign_to_id ? @assign_to_id : 0 + }, + {:onchange=>"remote_function();",:id=>"assigned_to_id",:name=>"v[assigned_to_id]",:class=>"w90"} + ) + %> + <%= select( :issue,:prior, [["低",1],["正常",2],["高",3],["紧急",4],["立刻",5]].unshift(["优先级",0]), + { :include_blank => false,:selected=>@priority_id ? @priority_id : 0 + }, + {:onchange=>"remote_function();",:id=>"priority_id",:name=>"v[priority_id]",:class=>"w90"} + ) + %> + <%= select( :issue,:status, [["新增",1],["正在解决",2],["已解决",3],["反馈",4],["关闭",5],["拒绝",6]].unshift(["状态",0]), + { :include_blank => false,:selected=>@status_id ? @status_id : 0 + }, + {:onchange=>"remote_function();",:id=>"status_id",:name=>"v[status_id]",:class=>"w90"} + ) + %> + <%= select( :issue,:user_id, @project.members.order("lower(users.login)").map{|c| [c.name, c.user_id]}.unshift(["作者",0]), + { :include_blank => false,:selected=>@author_id ? @author_id : 0 + }, + {:onchange=>"remote_function();",:id=>"author_id",:name=>"v[author_id]",:class=>"w90"} + ) + %> +
    +
    + <%# end %> +

    <%= l(:label_issues_sum) %>:<%= @project.issues.count %> + <%= l(:lable_issues_undo) %>:<%= @project.issues.where('status_id in (1,2,4,6)').count %> +

    + +
    <% end %> - <%= l(:label_issues_sum) %>:<%= @project.issues.count %> <%= l(:lable_issues_undo) %>:<%= @project.issues.where('status_id in (1,2,4,6)').count %>
    <% if !@query.new_record? && @query.editable_by?(User.current) %> @@ -23,51 +82,6 @@ <% html_title(@query.new_record? ? l(:label_issue_plural) : @query.name) %>
    - <%= form_tag({:controller => 'issues', :action => 'index', :project_id => @project}, :method => :get, :id => 'query_form', :class => 'query_form') do %> - <%= hidden_field_tag 'set_filter', '1' %> - -
    - ---<%= l :label_query_new %>--- - -
    -
    "> - - <%= l(:label_issue_query_condition) %> - -
    "> - <%= render :partial => 'queries/filters', :locals => {:query => @query} %> -
    -
    - -
    - <%= link_to_function l(:label_issue_query), 'submit_query_form("query_form")', :class => 'icon icon-checked' %> - <%= link_to l(:label_issue_cancel_query), {:set_filter => 1, :project_id => @project}, :class => 'icon icon-reload' %> -
    -
    -
    - <% end %>
    <%= error_messages_for 'query' %> @@ -78,12 +92,14 @@ <%= l(:label_no_data) %>

    <% else %> - <%= render :partial => 'issues/list', :locals => {:issues => @issues, :query => @query} %> -