Conflicts: .gitignore Gemfile app/views/issues/index.html.erb app/views/layouts/_base_feedback.html.erb app/views/tags/_tag_name.html.erb config/environments/production.rb db/schema.rb Signed-off-by: alan <547533434@qq.com>memcached_alan
commit
c80263a66e
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 << "<label>#{ check_box_tag name, course.id, false, :id => nil } #{h course.name}</label> [#{get_course_term course}]<br/>"
|
||||
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 << "<label>#{ check_box_tag name, project.id, false, :id => nil } #{h project.name}</label>"
|
||||
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 << "<label>#{ check_box_tag name, course.id, false, :id => nil } #{h course.name}</label> [#{get_course_term course}]<br/>"
|
||||
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 << "<label>#{ check_box_tag name, project.id, false, :id => nil } #{h project.name}</label><br/>"
|
||||
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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
|
||||
ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
|
||||
MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
|
||||
|
||||
def dispatch
|
||||
headers = [email.in_reply_to, email.references].flatten.compact
|
||||
subject = email.subject.to_s
|
||||
if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
|
||||
klass, object_id = $1, $2.to_i
|
||||
method_name = "receive_#{klass}_reply"
|
||||
if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
|
||||
send method_name, object_id
|
||||
else
|
||||
# ignoring it
|
||||
end
|
||||
elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
|
||||
receive_issue_reply(m[1].to_i)
|
||||
elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
|
||||
receive_message_reply(m[1].to_i)
|
||||
else
|
||||
dispatch_to_default
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid => 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{^<!DOCTYPE .*$}, ''
|
||||
@plain_text_body
|
||||
end
|
||||
|
||||
def cleaned_up_text_body
|
||||
cleanup_body(plain_text_body)
|
||||
end
|
||||
|
||||
def cleaned_up_subject
|
||||
subject = email.subject.to_s
|
||||
subject.strip[0,255]
|
||||
end
|
||||
|
||||
def self.full_sanitizer
|
||||
@full_sanitizer ||= HTML::FullSanitizer.new
|
||||
end
|
||||
|
||||
def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
|
||||
limit ||= object.class.columns_hash[attribute.to_s].limit || 255
|
||||
value = value.to_s.slice(0, limit)
|
||||
object.send("#{attribute}=", value)
|
||||
end
|
||||
|
||||
# Returns a User from an email address and a full name
|
||||
def self.new_user_from_attributes(email_address, fullname=nil)
|
||||
user = User.new
|
||||
|
||||
# Truncating the email address would result in an invalid format
|
||||
user.mail = email_address
|
||||
assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
|
||||
|
||||
names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
|
||||
assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
|
||||
assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
|
||||
user.lastname = '-' if user.lastname.blank?
|
||||
|
||||
password_length = [Setting.password_min_length.to_i, 10].max
|
||||
user.password = Redmine::Utils.random_hex(password_length / 2 + 1)
|
||||
user.language = Setting.default_language
|
||||
user.mail_notification = 'only_my_events'
|
||||
|
||||
unless user.valid?
|
||||
user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
|
||||
user.firstname = "-" unless user.errors[:firstname].blank?
|
||||
(puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
# Creates a User for the +email+ sender
|
||||
# Returns the user or nil if it could not be created
|
||||
def create_user_from_email
|
||||
from = email.header['from'].to_s
|
||||
addr, name = from, nil
|
||||
if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
|
||||
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{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
|
||||
ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
|
||||
MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
|
||||
|
||||
def dispatch
|
||||
headers = [email.in_reply_to, email.references].flatten.compact
|
||||
subject = email.subject.to_s
|
||||
if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
|
||||
klass, object_id = $1, $2.to_i
|
||||
method_name = "receive_#{klass}_reply"
|
||||
if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
|
||||
send method_name, object_id
|
||||
else
|
||||
# ignoring it
|
||||
end
|
||||
elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
|
||||
receive_issue_reply(m[1].to_i)
|
||||
elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
|
||||
receive_message_reply(m[1].to_i)
|
||||
else
|
||||
dispatch_to_default
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid => 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{^<!DOCTYPE .*$}, ''
|
||||
@plain_text_body
|
||||
end
|
||||
|
||||
def cleaned_up_text_body
|
||||
cleanup_body(plain_text_body)
|
||||
end
|
||||
|
||||
def cleaned_up_subject
|
||||
subject = email.subject.to_s
|
||||
subject.strip[0,255]
|
||||
end
|
||||
|
||||
def self.full_sanitizer
|
||||
@full_sanitizer ||= HTML::FullSanitizer.new
|
||||
end
|
||||
|
||||
def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
|
||||
limit ||= object.class.columns_hash[attribute.to_s].limit || 255
|
||||
value = value.to_s.slice(0, limit)
|
||||
object.send("#{attribute}=", value)
|
||||
end
|
||||
|
||||
# Returns a User from an email address and a full name
|
||||
def self.new_user_from_attributes(email_address, fullname=nil)
|
||||
user = User.new
|
||||
|
||||
# Truncating the email address would result in an invalid format
|
||||
user.mail = email_address
|
||||
assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
|
||||
|
||||
names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
|
||||
assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
|
||||
assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
|
||||
user.lastname = '-' if user.lastname.blank?
|
||||
|
||||
password_length = [Setting.password_min_length.to_i, 10].max
|
||||
user.password = Redmine::Utils.random_hex(password_length / 2 + 1)
|
||||
user.language = Setting.default_language
|
||||
user.mail_notification = 'only_my_events'
|
||||
|
||||
unless user.valid?
|
||||
user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
|
||||
user.firstname = "-" unless user.errors[:firstname].blank?
|
||||
(puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
# Creates a User for the +email+ sender
|
||||
# Returns the user or nil if it could not be created
|
||||
def create_user_from_email
|
||||
from = email.header['from'].to_s
|
||||
addr, name = from, nil
|
||||
if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
|
||||
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
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -0,0 +1,67 @@
|
||||
<span id="attachments_fields" xmlns="http://www.w3.org/1999/html">
|
||||
<% if defined?(container) && container && container.saved_attachments %>
|
||||
<% if isReply %>
|
||||
<% container.saved_attachments.each_with_index do |attachment, i| %>
|
||||
<span id="attachments_p<%= i %>" class="sub_btn">
|
||||
<%= 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"} %>
|
||||
<span class="ispublic-label"><%= l(:field_is_public)%>:</span>
|
||||
<%= 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}" %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<% container.attachments.each_with_index do |attachment, i| %>
|
||||
<span id="attachments_p<%= i %>" class="attachment">
|
||||
<%= 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"} %>
|
||||
<span class="ispublic-label"><%= l(:field_is_public)%>:</span>
|
||||
<%= 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}" %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</span>
|
||||
<script type='text/javascript'>
|
||||
// function CompatibleSend()
|
||||
// {
|
||||
// var obj=document.getElementById("_file");
|
||||
// var file= $(obj).clone();
|
||||
// file.click();
|
||||
// }
|
||||
</script>
|
||||
<span class="add_attachment">
|
||||
<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
|
||||
<!--%= link_to image_tag(),"javascript:void(0)", :onclick => "_file.click()"%-->
|
||||
<%= 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)
|
||||
} %>
|
||||
<span id="upload_file_count" :class="c_grey"><%= l(:label_no_file_uploaded)%></span>
|
||||
(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
|
||||
</span>
|
||||
|
||||
<% content_for :header_tags do %>
|
||||
<%= javascript_include_tag 'attachments' %>
|
||||
<% end %>
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
<div style="font-weight:normal;">
|
||||
<% for attachment in attachments %>
|
||||
<div title="<%= attachment.filename%>" id = "attachment_" style="max-width: 300px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;float: left;">
|
||||
<%= link_to_short_attachment attachment, :class => 'link_file', :download => true, :length => 100 -%>
|
||||
</div>
|
||||
<% if attachment.is_text? %>
|
||||
<div style="float: left;">
|
||||
<%= link_to image_tag('magnifier.png'),
|
||||
{:controller => 'attachments',
|
||||
:action => 'show',
|
||||
:id => attachment,
|
||||
:filename => attachment.filename},
|
||||
:target => "_blank"%>
|
||||
</div>
|
||||
<% end %>
|
||||
<br>
|
||||
<% end %>
|
||||
</div>
|
@ -0,0 +1,133 @@
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded',function(){
|
||||
var img = document.getElementsByName('issue_attachment_picture');
|
||||
|
||||
|
||||
function getImgNaturalStyle(img, callback) {
|
||||
var nWidth, nHeight;
|
||||
if (typeof img.naturalWidth == "undefined"|| img.naturalWidth == 0) {
|
||||
var image = new Image();
|
||||
image.src = img.src;
|
||||
if (image.complete) {
|
||||
callback(image);
|
||||
} else {
|
||||
image.onload = function () {
|
||||
callback(image);
|
||||
image.onload = null;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (img.complete) {
|
||||
nWidth = img.naturalWidth;
|
||||
nHeight = img.naturalHeight;
|
||||
} else {
|
||||
img.onload = function () {
|
||||
nWidth = img.naturalWidth;
|
||||
nHeight = img.naturalHeight;
|
||||
image.onload = null;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
return [nWidth, nHeight];
|
||||
}
|
||||
|
||||
|
||||
function UpdateImageInformation(image) {
|
||||
return [image.width,image.height];
|
||||
}
|
||||
|
||||
|
||||
for(i=0;i<img.length;i++)
|
||||
{
|
||||
imgNatural = getImgNaturalStyle(img[i],UpdateImageInformation);
|
||||
var width = imgNatural[0];
|
||||
var height = imgNatural[1];
|
||||
if (width<100)
|
||||
{
|
||||
img[i].width = width;
|
||||
}
|
||||
if (height<73) {
|
||||
img[i].height = height;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="attachments" style="font-weight:normal;">
|
||||
<% is_float ||= false %>
|
||||
<% for attachment in attachments %>
|
||||
<p style="width: 100%;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
|
||||
<%if is_float%>
|
||||
<div style="max-width:55%;white-space: nowrap; overflow: hidden; text-overflow: ellipsis;float: left;">
|
||||
<% end%>
|
||||
<span title="<%= attachment.filename %>" id = "attachment_">
|
||||
<% 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 %>
|
||||
</span>
|
||||
<%if is_float%>
|
||||
</div>
|
||||
<% end%>
|
||||
|
||||
<% if attachment.is_text? %>
|
||||
<%= link_to image_tag('magnifier.png'),
|
||||
:controller => 'attachments',
|
||||
:action => 'show',
|
||||
:id => attachment,
|
||||
:filename => attachment.filename%>
|
||||
<% end %>
|
||||
<span title="<%= attachment.description%>">
|
||||
<%= h(truncate(" - #{attachment.description}", length: options[:length] ? options[:length]:15, omission: '...')) unless attachment.description.blank? %>
|
||||
</span>
|
||||
<span class="size">(
|
||||
<%= number_to_human_size attachment.filesize %>)
|
||||
</span>
|
||||
<% 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] %>
|
||||
<br/>
|
||||
|
||||
<% end %>
|
||||
<% if options[:author] %>
|
||||
<span class="author" title="<%= attachment.author%>">
|
||||
<%= link_to h(truncate(attachment.author.name, length: 10, omission: '...')),user_path(attachment.author),:class => "c_orange" %>,
|
||||
<%= format_time(attachment.created_on) %>
|
||||
</span>
|
||||
<% end %>
|
||||
</p>
|
||||
<% end %>
|
||||
<div class="thumbnails">
|
||||
<% if defined?(thumbnails) && thumbnails %>
|
||||
<% images = attachments.select(&:thumbnailable?) %>
|
||||
<% if images.any? %>
|
||||
<% images.each do |attachment| %>
|
||||
<div class="pro_pic fl " width="100" height="73"><%= thumbnail_issue_tag(attachment) %></div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
|
||||
<% if @project %>
|
||||
<%= render :partial => 'project_show', locals: {project: @project} %>
|
||||
<% elsif @course %>
|
||||
<%= render :partial => 'course_show', locals: {course: @course} %>
|
||||
<% end %>
|
||||
|
||||
<% if @project %>
|
||||
<%= render :partial => 'project_show', locals: {project: @project} %>
|
||||
<% elsif @course %>
|
||||
<%= render :partial => 'course_show', locals: {course: @course} %>
|
||||
<% end %>
|
||||
|
@ -0,0 +1,28 @@
|
||||
<% selected_tab = params[:tab] ? params[:tab].to_s : tabs.first[:name] %>
|
||||
<div class="hwork_new">
|
||||
<div class="hwork_tb_">
|
||||
<ul>
|
||||
<% tabs.each do |tab| -%>
|
||||
<li><%= 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;" %></li>
|
||||
<% end -%>
|
||||
</ul>
|
||||
<!-- <div class="tabs-buttons" style="display:none;">
|
||||
<button class="tab-left" onclick="moveTabLeft(this);"></button>
|
||||
<button class="tab-right" onclick="moveTabRight(this);"></button>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(displayTabsButtons);
|
||||
$(window).resize(displayTabsButtons);
|
||||
</script>
|
||||
|
||||
<% 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 -%>
|
@ -1,31 +1,31 @@
|
||||
<%= javascript_include_tag "/assets/kindeditor/kindeditor" %>
|
||||
<div class="msg_box" id='leave-message'>
|
||||
<%# reply_allow = JournalsForMessage.create_by_user? User.current %>
|
||||
<h4><%= l(:label_leave_message) %></h4>
|
||||
|
||||
<% if !User.current.logged?%>
|
||||
<div style="font-size: 14px;margin:20px;">
|
||||
<%= l(:label_user_login_tips) %>
|
||||
<%= link_to l(:label_user_login_new), signin_path %>
|
||||
<hr/>
|
||||
</div>
|
||||
<% 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}%>
|
||||
<a href="javascript:void(0)" class="grey_btn fr ml10 mt10">取 消</a>
|
||||
<a href="javascript:void(0)" onclick='leave_message_editor.sync();$("#leave_message_form").submit();' class="blue_btn fr mt10">
|
||||
<%= l(:button_leave_meassge)%>
|
||||
</a>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div id="history">
|
||||
<%= render :partial => 'history',:locals => { :contest => @contest, :journals => @jour, :state => false} %>
|
||||
</div>
|
||||
<ul class="wlist">
|
||||
<%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%>
|
||||
<%= javascript_include_tag "/assets/kindeditor/kindeditor" %>
|
||||
<div class="msg_box" id='leave-message'>
|
||||
<%# reply_allow = JournalsForMessage.create_by_user? User.current %>
|
||||
<h4><%= l(:label_leave_message) %></h4>
|
||||
|
||||
<% if !User.current.logged?%>
|
||||
<div style="font-size: 14px;margin:20px;">
|
||||
<%= l(:label_user_login_tips) %>
|
||||
<%= link_to l(:label_user_login_new), signin_path %>
|
||||
<hr/>
|
||||
</div>
|
||||
<% 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}%>
|
||||
<a href="javascript:void(0)" class="grey_btn fr ml10 mt10">取 消</a>
|
||||
<a href="javascript:void(0)" onclick='leave_message_editor.sync();$("#leave_message_form").submit();' class="blue_btn fr mt10">
|
||||
<%= l(:button_leave_meassge)%>
|
||||
</a>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div id="history">
|
||||
<%= render :partial => 'history',:locals => { :contest => @contest, :journals => @jour, :state => false} %>
|
||||
</div>
|
||||
<ul class="wlist">
|
||||
<%= pagination_links_full @obj_pages, @obj_count, :per_page_links => false, :remote => false, :flag => true%>
|
||||
</ul>
|
@ -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 %>
|
@ -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("<span style='float: right;cursor:pointer;padding-left: 513px;'>" +
|
||||
"<a href='javascript:void(0)' onclick='hidden_homework_score_form();'><img src='/images/bid/close.png' width='26px' height='26px' /></a></span>");
|
||||
//$('#ajax-modal').parent().removeClass();
|
||||
$('#ajax-modal').parent().css("top","30%").css("left","40%").css("position","fixed");
|
||||
$('#ajax-modal').parent().addClass("new-watcher");
|
||||
|
@ -1,114 +1,114 @@
|
||||
<% bid = homework.bid%>
|
||||
<li class="pic_head" style="line-height: 1.2;">
|
||||
<% if is_student_batch_homework %>
|
||||
<!-- 学生匿评 不现实姓名、头像,以及相关的连接 -->
|
||||
<a><%= image_tag(url_to_avatar("匿名"), :width => "40", :height => "40")%></a>
|
||||
<a>匿名</a>
|
||||
<% else %>
|
||||
<%= link_to image_tag(url_to_avatar(homework.user), :width => "40", :height => "40"), user_path(homework.user) %>
|
||||
<span>
|
||||
<% user_realname = homework.user.lastname.to_s + homework.user.firstname.to_s %>
|
||||
<% user_name = is_teacher ? (user_realname.empty? ? homework.user.login : user_realname) : homework.user.login %>
|
||||
<%= link_to user_name, user_path(homework.user), :title => user_name %>
|
||||
</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="wname">
|
||||
<% if homework.name == nil || homework.name == "" %>
|
||||
<% homework_filename = homework.user.name + "提交的作品" %>
|
||||
<% else %>
|
||||
<% homework_filename = homework.name %>
|
||||
<% end %>
|
||||
<%= link_to homework_filename , homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type,:cur_sort => @cur_sort, :cur_direction => @cur_direction), :title => homework_filename, :remote => true%>
|
||||
<span class="c_grey ">
|
||||
提交时间:
|
||||
<%= format_time homework.created_at%>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wdown">
|
||||
<%= link_to "(#{homework.attachments.count.to_s}个附件)", zipdown_download_user_homework_path(:homework => homework)%>
|
||||
</li>
|
||||
<li class="wscore">
|
||||
<% unless is_student_batch_homework %>
|
||||
<%= l(:label_teacher_score)%>:
|
||||
<span class="c_red">
|
||||
<%= (homework.t_score.nil? || (homework.t_score && homework.t_score.to_i == 0)) ? l(:label_without_score) : format("%.2f",homework.t_score)%>
|
||||
</span>
|
||||
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="wscore">
|
||||
<%= is_student_batch_homework ? l(:label_my_score) : l(:label_student_score)%>:
|
||||
<span class="c_red">
|
||||
<%= is_student_batch_homework ? (homework.m_score.nil? ? l(:label_without_score) : format("%.2f",homework.m_score)) : (homework.s_score.nil? ? l(:label_without_score) : format("%.2f",homework.s_score))%>
|
||||
</span>
|
||||
</li>
|
||||
<% if is_teacher %>
|
||||
<!-- 是老师,所有列表正常显示 -->
|
||||
<li class="wping">
|
||||
<%= link_to l(:label_work_rating),homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type,:cur_sort => @cur_sort, :cur_direction => @cur_direction),:remote => true %>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% else %>
|
||||
<!-- 是学生 -->
|
||||
<% if is_my_homework %>
|
||||
<!-- 我的作品,在未开启匿评和未使用匿评,显示为编辑和删除 -->
|
||||
<% if bid.comment_status == 0 || bid.open_anonymous_evaluation == 0 || bid.comment_status == 2 %>
|
||||
<li class="wmine">
|
||||
<%= link_to l(:button_edit), edit_homework_attach_path(homework) %>
|
||||
<% if homework.user == User.current || User.current.admin? %>
|
||||
<!-- 作业创建者显示删除作业 -->
|
||||
<%= link_to(l(:label_bid_respond_delete), homework,
|
||||
method: :delete, :confirm => l(:text_are_you_sure), :remote => true ) %>
|
||||
<% else %>
|
||||
<!-- 作业参与者显示退出作业 -->
|
||||
<%= link_to l(:label_logout), destory_homework_users_homework_attach_path(homework,:user_id=>User.current.id),
|
||||
:remote => true, :confirm => l(:label_sure_exit_homework) %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="wmine" title="已开启匿评的作业不能进行修改和删除">
|
||||
<a style="color:#8e8e8e;"><%= l(:button_edit) %></a>
|
||||
<% if homework.user == User.current || User.current.admin? %>
|
||||
<!-- 作业创建者显示删除作业 -->
|
||||
<a style="color:#8e8e8e;">
|
||||
<%=l(:label_bid_respond_delete)%>
|
||||
</a>
|
||||
<% else %>
|
||||
<!-- 作业参与者显示退出作业 -->
|
||||
<a style="color:#8e8e8e;">
|
||||
<%=l(:label_logout) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% elsif is_student_batch_homework%>
|
||||
<!-- 学生匿评列表 -->
|
||||
<% if bid.comment_status == 1 %>
|
||||
<!-- 处于开启匿评阶段,可以正常评分 -->
|
||||
<li class="wping">
|
||||
<%= link_to l(:label_anonymous_comments),homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type),:remote => true %>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% elsif bid.comment_status == 2%>
|
||||
<!-- 处于匿评已关闭阶段,不容许评分 -->
|
||||
<li class="wping" title="关闭匿评后不可继续评分">
|
||||
<a style="background:#8e8e8e;">
|
||||
<%= l(:label_anonymous_comments) %>
|
||||
</a>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<!-- 学生众评列表,显示为点赞 -->
|
||||
<li class="wzan" id="homeworl_praise_li_<%= homework.id%>">
|
||||
<%= render :partial => "homework_attach/homework_praise", locals: {:homework => homework} %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% bid = homework.bid%>
|
||||
<li class="pic_head" style="line-height: 1.2;">
|
||||
<% if is_student_batch_homework %>
|
||||
<!-- 学生匿评 不现实姓名、头像,以及相关的连接 -->
|
||||
<a><%= image_tag(url_to_avatar("匿名"), :width => "40", :height => "40")%></a>
|
||||
<a>匿名</a>
|
||||
<% else %>
|
||||
<%= link_to image_tag(url_to_avatar(homework.user), :width => "40", :height => "40"), user_path(homework.user) %>
|
||||
<span>
|
||||
<% user_realname = homework.user.lastname.to_s + homework.user.firstname.to_s %>
|
||||
<% user_name = is_teacher ? (user_realname.empty? ? homework.user.login : user_realname) : homework.user.login %>
|
||||
<%= link_to user_name, user_path(homework.user), :title => user_name %>
|
||||
</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="wname">
|
||||
<% if homework.name == nil || homework.name == "" %>
|
||||
<% homework_filename = homework.user.name + "提交的作品" %>
|
||||
<% else %>
|
||||
<% homework_filename = homework.name %>
|
||||
<% end %>
|
||||
<%= link_to homework_filename , homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type,:cur_sort => @cur_sort, :cur_direction => @cur_direction), :title => homework_filename, :remote => true%>
|
||||
<span class="c_grey ">
|
||||
提交时间:
|
||||
<%= format_time homework.created_at%>
|
||||
</span>
|
||||
</li>
|
||||
<li class="wdown">
|
||||
<%= link_to "(#{homework.attachments.count.to_s}个附件)", zipdown_download_user_homework_path(:homework => homework)%>
|
||||
</li>
|
||||
<li class="wscore">
|
||||
<% unless is_student_batch_homework %>
|
||||
<%= l(:label_teacher_score)%>:
|
||||
<span class="c_red">
|
||||
<%= (homework.t_score.nil? || (homework.t_score && homework.t_score.to_i == 0)) ? l(:label_without_score) : format("%.2f",homework.t_score)%>
|
||||
</span>
|
||||
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="wscore">
|
||||
<%= is_student_batch_homework ? l(:label_my_score) : l(:label_student_score)%>:
|
||||
<span class="c_red">
|
||||
<%= is_student_batch_homework ? (homework.m_score.nil? ? l(:label_without_score) : format("%.2f",homework.m_score)) : (homework.s_score.nil? ? l(:label_without_score) : format("%.2f",homework.s_score))%>
|
||||
</span>
|
||||
</li>
|
||||
<% if is_teacher %>
|
||||
<!-- 是老师,所有列表正常显示 -->
|
||||
<li class="wping">
|
||||
<%= link_to l(:label_work_rating),homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type,:cur_sort => @cur_sort, :cur_direction => @cur_direction),:remote => true %>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% else %>
|
||||
<!-- 是学生 -->
|
||||
<% if is_my_homework %>
|
||||
<!-- 我的作品,在未开启匿评和未使用匿评,显示为编辑和删除 -->
|
||||
<% if bid.comment_status == 0 || bid.open_anonymous_evaluation == 0 || bid.comment_status == 2 %>
|
||||
<li class="wmine">
|
||||
<%= link_to l(:button_edit), edit_homework_attach_path(homework) %>
|
||||
<% if homework.user == User.current || User.current.admin? %>
|
||||
<!-- 作业创建者显示删除作业 -->
|
||||
<%= link_to(l(:label_bid_respond_delete), homework,
|
||||
method: :delete, :confirm => l(:text_are_you_sure), :remote => true ) %>
|
||||
<% else %>
|
||||
<!-- 作业参与者显示退出作业 -->
|
||||
<%= link_to l(:label_logout), destory_homework_users_homework_attach_path(homework,:user_id=>User.current.id),
|
||||
:remote => true, :confirm => l(:label_sure_exit_homework) %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="wmine" title="已开启匿评的作业不能进行修改和删除">
|
||||
<a style="color:#8e8e8e;"><%= l(:button_edit) %></a>
|
||||
<% if homework.user == User.current || User.current.admin? %>
|
||||
<!-- 作业创建者显示删除作业 -->
|
||||
<a style="color:#8e8e8e;">
|
||||
<%=l(:label_bid_respond_delete)%>
|
||||
</a>
|
||||
<% else %>
|
||||
<!-- 作业参与者显示退出作业 -->
|
||||
<a style="color:#8e8e8e;">
|
||||
<%=l(:label_logout) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% elsif is_student_batch_homework%>
|
||||
<!-- 学生匿评列表 -->
|
||||
<% if bid.comment_status == 1 %>
|
||||
<!-- 处于开启匿评阶段,可以正常评分 -->
|
||||
<li class="wping">
|
||||
<%= link_to l(:label_anonymous_comments),homework_attach_path(homework,:cur_page => @cur_page,:cur_type => @cur_type),:remote => true %>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% elsif bid.comment_status == 2%>
|
||||
<!-- 处于匿评已关闭阶段,不容许评分 -->
|
||||
<li class="wping" title="关闭匿评后不可继续评分">
|
||||
<a style="background:#8e8e8e;">
|
||||
<%= l(:label_anonymous_comments) %>
|
||||
</a>
|
||||
<% if Time.parse(bid.deadline.to_s).strftime("%Y-%m-%d") < Time.parse(homework.created_at.to_s).strftime("%Y-%m-%d") %>
|
||||
<span class="c_red"> 迟交!</span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<!-- 学生众评列表,显示为点赞 -->
|
||||
<li class="wzan" id="homeworl_praise_li_<%= homework.id%>">
|
||||
<%= render :partial => "homework_attach/homework_praise", locals: {:homework => homework} %>
|
||||
</li>
|
||||
<% end %>
|
||||
<% end %>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue