diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb
index d87bc6d2c..4525176ac 100644
--- a/app/controllers/attachments_controller.rb
+++ b/app/controllers/attachments_controller.rb
@@ -1,373 +1,373 @@
-# 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 AttachmentsController < ApplicationController
- layout "users_base"
- before_filter :find_project, :only => [:show, :download, :thumbnail, :destroy, :delete_homework]#, :except => [:upload, :autocomplete]
- before_filter :file_readable, :read_authorize, :only => [:show, :thumbnail]#Modified by young
- before_filter :delete_authorize, :only => :destroy
- before_filter :authorize_global, :only => :upload
-
- before_filter :login_without_softapplication, only: [:download]
- accept_api_auth :show, :download, :upload
- require 'iconv'
-
-
- def show
- respond_to do |format|
- format.html {
- if @attachment.is_diff?
- @diff = File.new(@attachment.diskfile, "rb").read
- @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
- @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
- # Save diff type as user preference
- if User.current.logged? && @diff_type != User.current.pref[:diff_type]
- User.current.pref[:diff_type] = @diff_type
- User.current.preference.save
- end
- render :action => 'diff'
- elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
- @content = File.new(@attachment.diskfile, "rb").read
- # 编码为非 UTF-8先进行间接转码
- # 部分unicode编码不直接支持转为 UTF-8
- # modify by nwb
- if @content.encoding.name != 'UTF-8'
- @content = @content.force_encoding('GBK')
- @content = @content.encode('UTF-8')
- end
- render :action => 'file'
- else
- download
- end
- }
- format.api
- end
- end
-
- def download
- # modify by nwb
- # 下载添加权限设置
- candown = false
- if (@attachment.container.has_attribute?(:project) || @attachment.container.has_attribute?(:project_id)) && @attachment.container.project
- project = @attachment.container.project
- candown= User.current.member_of?(project) || (project.is_public && @attachment.is_public == 1)
- elsif @attachment.container.is_a?(Project)
- project = @attachment.container
- candown= User.current.member_of?(project) || (project.is_public && @attachment.is_public == 1)
- elsif (@attachment.container.has_attribute?(:course) ||@attachment.container.has_attribute?(:course_id) ) && @attachment.container.course
- course = @attachment.container.course
- candown= User.current.member_of_course?(course) || (course.is_public==1 && @attachment.is_public == 1)
- elsif @attachment.container.is_a?(Course)
- course = @attachment.container
- candown= User.current.member_of_course?(course) || (course.is_public==1 && @attachment.is_public == 1)
- elsif @attachment.container.class.to_s=="HomeworkAttach" && @attachment.container.bid.reward_type == 3
- candown = true
- else
- candown = @attachment.is_public == 1
- end
- if candown || User.current.admin? || User.current.id == @attachment.author_id
- @attachment.increment_download
-
- if stale?(:etag => @attachment.digest)
- # images are sent inline
- send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
- :type => detect_content_type(@attachment),
- :disposition => (@attachment.image? ? 'inline' : 'attachment')
- end
-
- else
- render_403 :message => :notice_not_authorized
- end
-
- rescue => e
- redirect_to "http://" + (Setting.host_name.to_s) +"/file_not_found.html"
- end
-
- #更新资源文件类型
- def updateType
- @attachment = Attachment.find(params[:attachmentid])
- if @attachment != nil
- @attachment.attachtype = params[:newtype]
- @attachment.save
- render :text =>'success'
- else
- render :text=>'error'
- end
- end
-
- # 更新文件密级
- def updateFileDense
- @attachment = Attachment.find(params[:attachmentid])
- if @attachment != nil
- filedense = params[:newtype].to_s
- # d = Iconv.conv("unicodebig","utf-8",filedense)
- if filedense == "%E5%85%AC%E5%BC%80" #l(:field_is_public)
- @attachment.is_public = 1
- else
- @attachment.is_public = 0
- end
- @attachment.save
- @newfiledense = filedense
- end
- respond_to do |format|
- format.js
- end
- end
-
- def thumbnail
- if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
- if stale?(:etag => thumbnail)
- send_file thumbnail,
- :filename => filename_for_content_disposition(@attachment.filename),
- :type => detect_content_type(@attachment),
- :disposition => 'inline'
- end
- else
- # No thumbnail for the attachment or thumbnail could not be created
- render :nothing => true, :status => 404
- end
- end
-
-
- def upload
- # Make sure that API users get used to set this content type
- # as it won't trigger Rails' automatic parsing of the request body for parameters
- unless request.content_type == 'application/octet-stream'
- render :nothing => true, :status => 406
- return
- end
-
- @attachment = Attachment.new(:file => request.raw_post)
- @attachment.author = User.current
- @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
- saved = @attachment.save
-
- respond_to do |format|
- format.js
- format.api {
- if saved
- render :action => 'upload', :status => :created
- else
- render_validation_errors(@attachment)
- end
- }
- end
- end
-
- def destroy
- if @attachment.container.respond_to?(:init_journal)
- @attachment.container.init_journal(User.current)
- end
- if @attachment.container
- # Make sure association callbacks are called
- @attachment.container.attachments.delete(@attachment)
- else
- @attachment.destroy
- end
-
- respond_to do |format|
- if !@attachment.container.nil? &&
- (@attachment.container.is_a?(Course) || ((@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course_id) ) &&
- @attachment.container.course ) || ((@attachment.container.has_attribute?(:board) || @attachment.container.has_attribute?(:board_id)) &&
- @attachment.container.board && @attachment.container.board.course ) )
- if @attachment.container.is_a?(News)
- format.html { redirect_to_referer_or news_path(@attachment.container) }
- elsif @attachment.container.is_a?(Message)
- format.html { redirect_to_referer_or new_board_message_path(@attachment.container) }
- elsif @course.nil?
- format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) }
- else
- format.html { redirect_to_referer_or course_path(@course) }
- end
- else
- if @project.nil?
- format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) }
- else
- format.html { redirect_to_referer_or project_path(@project) }
- end
- end
-
- format.js
- end
- end
-
- def delete_homework
- @bid = @attachment.container.bid
- # Make sure association callbacks are called
- container = @attachment.container
- @attachment.container.attachments.delete(@attachment)
- #if container.attachments.empty?
- #container.delete
- #end
-
- respond_to do |format|
- format.html { redirect_to_referer_or respond_path(@bid) }
- format.js
- end
- end
-
- #删除竞赛作品的附件
- def delete_softapplications
- @attachment = Attachment.find params[:id]
- @softapplication = @attachment.container if @attachment!=nil
- @attachment.container.attachments.delete(@attachment) if @attachment!=nil
- respond_to do |format|
- format.html { redirect_to_referer_or edit_softapplication_path(@softapplication) }
- #format.js
- end
- end
-
- def autocomplete
- # modify by nwb
- if params[:project_id]
- @project = Project.find_by_id(params[:project_id])
- elsif params[:course_id]
- @course = Course.find_by_id(params[:course_id])
- end
-
- respond_to do |format|
- format.js
- end
- end
-
- def add_exist_file_to_project
- classname = params[:class_name]
- class_id = params[:class_id]
- attachments = params[:attachment][:attach]
-
- obj = Object.const_get(classname).find_by_id(class_id)
- attachments.collect do |attach_id|
- ori = Attachment.find_by_id(attach_id)
- next if ori.blank?
- attach_copied_obj = ori.copy
- attach_copied_obj.tag_list.add(ori.tag_list) # tag关联
- attach_copied_obj.container = obj
- attach_copied_obj.created_on = Time.now
- attach_copied_obj.author_id = User.current.id
- if attach_copied_obj.attachtype == nil
- attach_copied_obj.attachtype = 1
- end
- @obj = obj
- @save_flag = attach_copied_obj.save
- @save_message = attach_copied_obj.errors.full_messages
- end
-
- respond_to do |format|
- format.js
- end
- rescue NoMethodError
- @save_flag = false
- @save_message = [] << l(:error_attachment_empty)
- respond_to do |format|
- format.js
- end
- end
-
- def add_exist_file_to_course
- class_id = params[:class_id]
- attachments = params[:attachment][:attach]
-
- obj = Course.find_by_id(class_id)
- attachments.collect do |attach_id|
- ori = Attachment.find_by_id(attach_id)
- next if ori.blank?
- attach_copied_obj = ori.copy
- attach_copied_obj.tag_list.add(ori.tag_list) # tag关联
- attach_copied_obj.container = obj
- attach_copied_obj.created_on = Time.now
- attach_copied_obj.author_id = User.current.id
- if attach_copied_obj.attachtype == nil
- attach_copied_obj.attachtype = 4
- end
- @obj = obj
- @save_flag = attach_copied_obj.save
- @save_message = attach_copied_obj.errors.full_messages
- end
-
- respond_to do |format|
- format.js
- end
- rescue NoMethodError
- @save_flag = false
- @save_message = [] << l(:error_attachment_empty)
- respond_to do |format|
- format.js
- end
- end
-
-private
- def find_project
- @attachment = Attachment.find(params[:id])
- # Show 404 if the filename in the url is wrong
- # modify by nwb
- raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
- if @attachment.container_type == 'Course'
- @course = @attachment.course
- elsif !@attachment.container.nil? && (@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course)) && @attachment.container.course
- @course = @attachment.container.course
- else
- unless @attachment.container_type == 'Bid' || @attachment.container_type == 'HomeworkAttach' || @attachment.container_type == 'Memo' || @attachment.container_type == 'Softapplication'
- @project = @attachment.project
- end
- end
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- # Checks that the file exists and is readable
- def file_readable
- if @attachment.readable?
- true
- else
- logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
- render_404
- end
- end
-
- def read_authorize
- if @attachment.container_type == "HomeworkAttach" || @attachment.container_type == 'Bid'
- true
- #User.current.allowed_to?(:view_homework_attaches, @attachment.project) ? true : deny_access
- else
- @attachment.visible? ? true : deny_access
- end
- end
-
- def delete_authorize
- @attachment.deletable? ? true : deny_access
- end
-
- def detect_content_type(attachment)
- content_type = attachment.content_type
- if content_type.blank?
- content_type = Redmine::MimeType.of(attachment.filename)
- end
- content_type.to_s
- end
-
- def login_without_softapplication
- referer = request.headers['Referer']
- require_login unless referer =~ /softapplication/
- end
-
- def renderTag
- @attachmentNew = Attachment.find(params[:attchmentId])
- respond_to do |format|
- format.js
- 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 AttachmentsController < ApplicationController
+ layout "users_base"
+ before_filter :find_project, :only => [:show, :download, :thumbnail, :destroy, :delete_homework]#, :except => [:upload, :autocomplete]
+ before_filter :file_readable, :read_authorize, :only => [:show, :thumbnail]#Modified by young
+ before_filter :delete_authorize, :only => :destroy
+ before_filter :authorize_global, :only => :upload
+
+ before_filter :login_without_softapplication, only: [:download]
+ accept_api_auth :show, :download, :upload
+ require 'iconv'
+
+
+ def show
+ respond_to do |format|
+ format.html {
+ if @attachment.is_diff?
+ @diff = File.new(@attachment.diskfile, "rb").read
+ @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
+ @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
+ # Save diff type as user preference
+ if User.current.logged? && @diff_type != User.current.pref[:diff_type]
+ User.current.pref[:diff_type] = @diff_type
+ User.current.preference.save
+ end
+ render :action => 'diff'
+ elsif @attachment.is_text? && @attachment.filesize <= Setting.file_max_size_displayed.to_i.kilobyte
+ @content = File.new(@attachment.diskfile, "rb").read
+ # 编码为非 UTF-8先进行间接转码
+ # 部分unicode编码不直接支持转为 UTF-8
+ # modify by nwb
+ if @content.encoding.name != 'UTF-8'
+ @content = @content.force_encoding('GBK')
+ @content = @content.encode('UTF-8')
+ end
+ render :action => 'file'
+ else
+ download
+ end
+ }
+ format.api
+ end
+ end
+
+ def download
+ # modify by nwb
+ # 下载添加权限设置
+ candown = false
+ if (@attachment.container.has_attribute?(:project) || @attachment.container.has_attribute?(:project_id)) && @attachment.container.project
+ project = @attachment.container.project
+ candown= User.current.member_of?(project) || (project.is_public && @attachment.is_public == 1)
+ elsif @attachment.container.is_a?(Project)
+ project = @attachment.container
+ candown= User.current.member_of?(project) || (project.is_public && @attachment.is_public == 1)
+ elsif (@attachment.container.has_attribute?(:course) ||@attachment.container.has_attribute?(:course_id) ) && @attachment.container.course
+ course = @attachment.container.course
+ candown= User.current.member_of_course?(course) || (course.is_public==1 && @attachment.is_public == 1)
+ elsif @attachment.container.is_a?(Course)
+ course = @attachment.container
+ candown= User.current.member_of_course?(course) || (course.is_public==1 && @attachment.is_public == 1)
+ elsif @attachment.container.class.to_s=="HomeworkAttach" && @attachment.container.bid.reward_type == 3
+ candown = true
+ else
+ candown = @attachment.is_public == 1
+ end
+ if candown || User.current.admin? || User.current.id == @attachment.author_id
+ @attachment.increment_download
+
+ if stale?(:etag => @attachment.digest)
+ # images are sent inline
+ send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
+ :type => detect_content_type(@attachment),
+ :disposition => (@attachment.image? ? 'inline' : 'attachment')
+ end
+
+ else
+ render_403 :message => :notice_not_authorized
+ end
+
+ rescue => e
+ redirect_to "http://" + (Setting.host_name.to_s) +"/file_not_found.html"
+ end
+
+ #更新资源文件类型
+ def updateType
+ @attachment = Attachment.find(params[:attachmentid])
+ if @attachment != nil
+ @attachment.attachtype = params[:newtype]
+ @attachment.save
+ render :text =>'success'
+ else
+ render :text=>'error'
+ end
+ end
+
+ # 更新文件密级
+ def updateFileDense
+ @attachment = Attachment.find(params[:attachmentid])
+ if @attachment != nil
+ filedense = params[:newtype].to_s
+ # d = Iconv.conv("unicodebig","utf-8",filedense)
+ if filedense == "%E5%85%AC%E5%BC%80" #l(:field_is_public)
+ @attachment.is_public = 1
+ else
+ @attachment.is_public = 0
+ end
+ @attachment.save
+ @newfiledense = filedense
+ end
+ respond_to do |format|
+ format.js
+ end
+ end
+
+ def thumbnail
+ if @attachment.thumbnailable? && thumbnail = @attachment.thumbnail(:size => params[:size])
+ if stale?(:etag => thumbnail)
+ send_file thumbnail,
+ :filename => filename_for_content_disposition(@attachment.filename),
+ :type => detect_content_type(@attachment),
+ :disposition => 'inline'
+ end
+ else
+ # No thumbnail for the attachment or thumbnail could not be created
+ render :nothing => true, :status => 404
+ end
+ end
+
+
+ def upload
+ # Make sure that API users get used to set this content type
+ # as it won't trigger Rails' automatic parsing of the request body for parameters
+ unless request.content_type == 'application/octet-stream'
+ render :nothing => true, :status => 406
+ return
+ end
+
+ @attachment = Attachment.new(:file => request.raw_post)
+ @attachment.author = User.current
+ @attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16)
+ saved = @attachment.save
+
+ respond_to do |format|
+ format.js
+ format.api {
+ if saved
+ render :action => 'upload', :status => :created
+ else
+ render_validation_errors(@attachment)
+ end
+ }
+ end
+ end
+
+ def destroy
+ if @attachment.container.respond_to?(:init_journal)
+ @attachment.container.init_journal(User.current)
+ end
+ if @attachment.container
+ # Make sure association callbacks are called
+ @attachment.container.attachments.delete(@attachment)
+ else
+ @attachment.destroy
+ end
+
+ respond_to do |format|
+ if !@attachment.container.nil? &&
+ (@attachment.container.is_a?(Course) || ((@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course_id) ) &&
+ @attachment.container.course ) || ((@attachment.container.has_attribute?(:board) || @attachment.container.has_attribute?(:board_id)) &&
+ @attachment.container.board && @attachment.container.board.course ) )
+ if @attachment.container.is_a?(News)
+ format.html { redirect_to_referer_or news_path(@attachment.container) }
+ elsif @attachment.container.is_a?(Message)
+ format.html { redirect_to_referer_or new_board_message_path(@attachment.container) }
+ elsif @course.nil?
+ format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) }
+ else
+ format.html { redirect_to_referer_or course_path(@course) }
+ end
+ else
+ if @project.nil?
+ format.html { redirect_to_referer_or forum_memo_path(@attachment.container.forum, @attachment.container) }
+ else
+ format.html { redirect_to_referer_or project_path(@project) }
+ end
+ end
+
+ format.js
+ end
+ end
+
+ def delete_homework
+ @bid = @attachment.container.bid
+ # Make sure association callbacks are called
+ container = @attachment.container
+ @attachment.container.attachments.delete(@attachment)
+ #if container.attachments.empty?
+ #container.delete
+ #end
+
+ respond_to do |format|
+ format.html { redirect_to_referer_or respond_path(@bid) }
+ format.js
+ end
+ end
+
+ #删除竞赛作品的附件
+ def delete_softapplications
+ @attachment = Attachment.find params[:id]
+ @softapplication = @attachment.container if @attachment!=nil
+ @attachment.container.attachments.delete(@attachment) if @attachment!=nil
+ respond_to do |format|
+ format.html { redirect_to_referer_or edit_softapplication_path(@softapplication) }
+ #format.js
+ end
+ end
+
+ def autocomplete
+ # modify by nwb
+ if params[:project_id]
+ @project = Project.find_by_id(params[:project_id])
+ elsif params[:course_id]
+ @course = Course.find_by_id(params[:course_id])
+ end
+
+ respond_to do |format|
+ format.js
+ end
+ end
+
+ def add_exist_file_to_project
+ classname = params[:class_name]
+ class_id = params[:class_id]
+ attachments = params[:attachment][:attach]
+
+ obj = Object.const_get(classname).find_by_id(class_id)
+ attachments.collect do |attach_id|
+ ori = Attachment.find_by_id(attach_id)
+ next if ori.blank?
+ attach_copied_obj = ori.copy
+ attach_copied_obj.tag_list.add(ori.tag_list) # tag关联
+ attach_copied_obj.container = obj
+ attach_copied_obj.created_on = Time.now
+ attach_copied_obj.author_id = User.current.id
+ if attach_copied_obj.attachtype == nil
+ attach_copied_obj.attachtype = 1
+ end
+ @obj = obj
+ @save_flag = attach_copied_obj.save
+ @save_message = attach_copied_obj.errors.full_messages
+ end
+
+ respond_to do |format|
+ format.js
+ end
+ rescue NoMethodError
+ @save_flag = false
+ @save_message = [] << l(:error_attachment_empty)
+ respond_to do |format|
+ format.js
+ end
+ end
+
+ def add_exist_file_to_course
+ class_id = params[:class_id]
+ attachments = params[:attachment][:attach]
+
+ obj = Course.find_by_id(class_id)
+ attachments.collect do |attach_id|
+ ori = Attachment.find_by_id(attach_id)
+ next if ori.blank?
+ attach_copied_obj = ori.copy
+ attach_copied_obj.tag_list.add(ori.tag_list) # tag关联
+ attach_copied_obj.container = obj
+ attach_copied_obj.created_on = Time.now
+ attach_copied_obj.author_id = User.current.id
+ if attach_copied_obj.attachtype == nil
+ attach_copied_obj.attachtype = 4
+ end
+ @obj = obj
+ @save_flag = attach_copied_obj.save
+ @save_message = attach_copied_obj.errors.full_messages
+ end
+
+ respond_to do |format|
+ format.js
+ end
+ rescue NoMethodError
+ @save_flag = false
+ @save_message = [] << l(:error_attachment_empty)
+ respond_to do |format|
+ format.js
+ end
+ end
+
+private
+ def find_project
+ @attachment = Attachment.find(params[:id])
+ # Show 404 if the filename in the url is wrong
+ # modify by nwb
+ raise ActiveRecord::RecordNotFound if params[:filename] && params[:filename] != @attachment.filename
+ if @attachment.container_type == 'Course'
+ @course = @attachment.course
+ elsif !@attachment.container.nil? && (@attachment.container.has_attribute?(:course) || @attachment.container.has_attribute?(:course)) && @attachment.container.course
+ @course = @attachment.container.course
+ else
+ unless @attachment.container_type == 'Bid' || @attachment.container_type == 'HomeworkAttach' || @attachment.container_type == 'Memo' || @attachment.container_type == 'Softapplication'
+ @project = @attachment.project
+ end
+ end
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ # Checks that the file exists and is readable
+ def file_readable
+ if @attachment.readable?
+ true
+ else
+ logger.error "Cannot send attachment, #{@attachment.diskfile} does not exist or is unreadable."
+ render_404
+ end
+ end
+
+ def read_authorize
+ if @attachment.container_type == "HomeworkAttach" || @attachment.container_type == 'Bid'
+ true
+ #User.current.allowed_to?(:view_homework_attaches, @attachment.project) ? true : deny_access
+ else
+ @attachment.visible? ? true : deny_access
+ end
+ end
+
+ def delete_authorize
+ @attachment.deletable? ? true : deny_access
+ end
+
+ def detect_content_type(attachment)
+ content_type = attachment.content_type
+ if content_type.blank?
+ content_type = Redmine::MimeType.of(attachment.filename)
+ end
+ content_type.to_s
+ end
+
+ def login_without_softapplication
+ referer = request.headers['Referer']
+ require_login unless referer =~ /softapplication/
+ end
+
+ def renderTag
+ @attachmentNew = Attachment.find(params[:attchmentId])
+ respond_to do |format|
+ format.js
+ end
+ end
+end
diff --git a/app/controllers/contests_controller.rb b/app/controllers/contests_controller.rb
index e2f5e3731..b099a1db5 100644
--- a/app/controllers/contests_controller.rb
+++ b/app/controllers/contests_controller.rb
@@ -2,33 +2,44 @@
# class BidsController < ApplicationController
class ContestsController < ApplicationController
layout "contest_base"
+
menu_item :respond
menu_item :project, :only => :show_project
menu_item :application, :only => :show_softapplication
menu_item :attendingcontests, :only => :show_attendingcontest
menu_item :contestnotifications, :only => :index
- before_filter :can_show_contest,except: []
- before_filter :find_contest, :only => [:show_contest, :show_project, :show_softapplication, :show_attendingcontest, :index, :set_reward_project, :set_reward_softapplication, :create,:destroy,:more,:back,:add,:add_softapplication,:new,:show_results, :set_reward,
- :show_contest_project, :show_contest_user, :join_in_contest, :unjoin_in_contest, :new_join,:show_participator, :settings]
-
+ before_filter :can_show_contest, :except => [] # modified by alan
+
+ # modified by longjun
+ before_filter :find_contest, :only => [
+ :show_contest, :show_project, :show_softapplication,
+ :show_attendingcontest, :index, :set_reward_project,
+ :set_reward_softapplication, :create, :destroy, :more,
+ :back, :add, :add_softapplication, :new,:show_results,
+ :set_reward, :show_contest_project, :show_contest_user,
+ :join_in_contest, :unjoin_in_contest, :new_join, :show_participator, :settings
+ ]
+ # end longjun
+
# added by fq
before_filter :require_login, :only => [:join_in_contest, :unjoin_in_contest]
# end
- before_filter :require_login,:only => [:set_reward, :destroy, :add, :new, ]
+ before_filter :require_login,:only => [:set_reward, :destroy, :add, :new ]
helper :watchers
helper :attachments
- include AttachmentsHelper
- include ApplicationHelper
helper :projects
helper :words
+ include AttachmentsHelper
+ include ApplicationHelper
+
def index
# @contests = Contest.visible
# @contests ||= []
- @offset, @limit = api_offset_and_limit({:limit => 10})
+ @offset, @limit = api_offset_and_limit(:limit => 10)
#@contests = Contest.visible
#@contests = @contests.like(params[:name]) if params[:name].present?
@contests = Contest.visible.where("name like '%#{params[:name]}%'")
@@ -41,43 +52,55 @@ class ContestsController < ApplicationController
@offset ||= @contest_pages.reverse_offset
if params[:contest_sort_type].present?
case params[:contest_sort_type]
- when '0'
- unless @offset == 0
- @contests = @contests.reorder('contests.commit').offset(@offset).limit(@limit).all.reverse
- else
- limit = @contest_count % @limit
- limit = @limit if limit == 0
- @contests = @contests.reorder('contests.commit').offset(@offset).limit(limit).all.reverse
- end
- @s_state = 0
- when '1'
- unless @offset == 0
- @contests = @contests.reorder('contests.created_on').offset(@offset).limit(@limit).all.reverse
- else
- limit = @contest_count % @limit
- limit = @limit if limit == 0
- @contests = @contests.reorder('contests.created_on').offset(@offset).limit(limit).all.reverse
- end
- @s_state = 1
- when '2'
- unless @offset == 0
- @contests = @contests.offset(@offset).limit(@limit).all.reverse
- else
- limit = @contest_count % @limit
- limit = @limit if limit == 0
- @contests = @contests.offset(@offset).limit(@limit).all.reverse
- end
- @s_state = 0
+ when '0'
+ # modified by longjun
+ # never use unless and else
+ # unless @offset == 0
+ if @offset != 0
+ @contests = @contests.reorder('contests.commit').offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @contest_count % @limit
+ limit = @limit if limit == 0
+ @contests = @contests.reorder('contests.commit').offset(@offset).limit(limit).all.reverse
end
+ @s_state = 0
+ when '1'
+ # modified by longjun
+ # never use unless and else
+ # unless @offset == 0
+ if @offset != 0
+ @contests = @contests.reorder('contests.created_on').offset(@offset).limit(@limit).all.reverse
else
- unless @offset == 0
- @contests = @contests.reorder('contests.created_on').offset(@offset).limit(@limit).all.reverse
- else
- limit = @contest_count % @limit
- limit = @limit if limit == 0
- @contests = @contests.reorder('contests.created_on').offset(@offset).limit(limit).all.reverse
- end
- @s_state = 1
+ limit = @contest_count % @limit
+ limit = @limit if limit == 0
+ @contests = @contests.reorder('contests.created_on').offset(@offset).limit(limit).all.reverse
+ end
+ @s_state = 1
+ when '2'
+ # modified by longjun
+ # never use unless and else
+ # unless @offset == 0
+ if @offset != 0
+ @contests = @contests.offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @contest_count % @limit
+ limit = @limit if limit == 0
+ @contests = @contests.offset(@offset).limit(@limit).all.reverse
+ end
+ @s_state = 0
+ end
+ else
+ # modified by longjun
+ # never use unless and else
+ # unless @offset == 0
+ if @offset != 0
+ @contests = @contests.reorder('contests.created_on').offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @contest_count % @limit
+ limit = @limit if limit == 0
+ @contests = @contests.reorder('contests.created_on').offset(@offset).limit(limit).all.reverse
+ end
+ @s_state = 1
end
end
@@ -93,7 +116,10 @@ class ContestsController < ApplicationController
@bid_pages = Paginator.new @bid_count, @limit, params['page']
@offset ||= @bid_pages.reverse_offset
- unless @offset == 0
+ # modified by longjun
+ # never use unless and else
+ # unless @offset == 0
+ if @offset != 0
@bids = @bids.offset(@offset).limit(@limit).all.reverse
else
limit = @bid_count % @limit
@@ -149,10 +175,7 @@ class ContestsController < ApplicationController
end
end
- def new_join
-
- end
def show_participator
render :layout => 'base_newcontest'
@@ -186,26 +209,43 @@ class ContestsController < ApplicationController
def show_contest_project
contests = Contest.where('parent_id = ?', @contest.id)
@projects = []
- for contest in contests
- @projects += contest.contesting_projects
- end
+
+ # Modified by longjun
+ # 用 arr.each 替换 for [ according to the style guide ]
+
+ # for contest in contests
+ # @projects += contest.contesting_projects
+ # end
+
+ contests.each do |contest|
+ @projects += contest.contesting_projects
+ end
+
+ # end
respond_to do |format|
format.html {
render :layout => 'base_newcontest'
}
format.api
-
end
end
def show_contest_softapplication
contests = Contest.where('parent_id = ?', @contest.id)
@softapplications = []
- for contest in contests
- @softapplications += contest.contesting_softapplications
- end
-
+
+ # Modified by Longjun
+ # for contest in contests
+ # @softapplications += contest.contesting_softapplications
+
+
+ contests.each do |contest|
+ @softapplications += contest.contesting_softapplications
+ end
+
+ # end
+
respond_to do |format|
format.html {
render :layout => 'base_newcontest'
@@ -218,12 +258,21 @@ class ContestsController < ApplicationController
def show_contest_user
contests = Contest.find(:all)
@users = []
- for contest in contests
- for project in contest.projects
- @users += project.users
- end
+
+ # Modified by Longjun
+ # for contest in contests
+ # for project in contest.projects
+ # @users += project.users
+ # end
+
+
+ contests.each do |contest|
+ contest.projects.each do |project|
+ @uers += project.users
+ end
end
-
+ # end
+
respond_to do |format|
format.html {
render :layout => 'base_newcontest'
@@ -239,11 +288,20 @@ class ContestsController < ApplicationController
# @contesting_project_count = @contesting_project_all.count
# @contesting_project_pages = Paginator.new @contesting_project_count, per_page_option, params['page']
@membership.each do |membership|
- unless(membership.project.project_type==1)
- if User.current.allowed_to?(:quote_project, membership.project)
+
+ # Modified by Longjun
+ # 将两个判断语句合并
+ # unless membership.project.project_type==1
+ # if User.current.allowed_to?(:quote_project, membership.project)
+ # @option << membership.project
+ # end
+ # end
+ if membership.project.project_type != 1 && User.current.allowed_to?(:quote_project, membership.project)
@option << membership.project
- end
+
end
+ # end
+
end
@user = @contest.author
@contesting_project = @contest.contesting_projects.all
@@ -262,16 +320,19 @@ class ContestsController < ApplicationController
@temp = []
@contesting_project.each do |pro|
- if pro.project && pro.project.project_status
- @temp << pro
- end
+ # modified by longjun
+ # if pro.project && pro.project.project_status
+ # @temp << pro
+ # end
+ @temp << pro if pro.project && pro.project.project_status
+ # end longjun
@temp
end
if @temp.size > 0
@contesting_project = @temp.sort {|a,b| b.project.project_status.grade <=> a.project.project_status.grade}
end
end
- @contesting_project = paginateHelper @contesting_project
+ @contesting_project = paginateHelper(@contesting_project)
respond_to do |format|
format.html {
render :layout => 'base_newcontest'
@@ -290,7 +351,7 @@ class ContestsController < ApplicationController
@softapplication = Softapplication.all
@contesting_softapplication = @contest.contesting_softapplications
- @contesting_softapplication = paginateHelper @contesting_softapplication, 10
+ @contesting_softapplication = paginateHelper(@contesting_softapplication, 10)
# @temp = []
# @softapplicationt.each do |pro|
@@ -328,7 +389,7 @@ class ContestsController < ApplicationController
# @contesting_project_count = @contesting_project_all.count
# @contesting_project_pages = Paginator.new @contesting_project_count, per_page_option, params['page']
@membership.each do |membership|
- unless(membership.project.project_type==1)
+ unless membership.project.project_type==1
#拥有编辑项目权限的可将该项目参赛
if User.current.allowed_to?(:quote_project, membership.project)
@option << membership.project
@@ -352,27 +413,31 @@ class ContestsController < ApplicationController
@temp = []
@contesting_project.each do |pro|
- if pro.project && pro.project.project_status
- @temp << pro
- end
+ # modified by longjun
+ # if pro.project && pro.project.project_status
+ # @temp << pro
+ # end
+ @temp << pro if pro.project && pro.project.project_status
+ # end longjun
@temp
end
if @temp.size > 0
@contesting_project = @temp.sort {|a,b| b.project.project_status.grade <=> a.project.project_status.grade}
end
end
-##取出参赛应用 --应用列表
+ # 取出参赛应用 --应用列表
@softapplication = Softapplication.all
@contesting_softapplication = @contest.contesting_softapplications.
joins("LEFT JOIN softapplications ON contesting_softapplications.softapplication_id=softapplications.id").
joins("LEFT JOIN (
- SELECT * FROM seems_rateable_cached_ratings WHERE cacheable_type='Softapplication' AND DIMENSION = 'quality') AS cached
- ON cached.cacheable_id=softapplications.id").
+ SELECT * FROM seems_rateable_cached_ratings
+ WHERE cacheable_type='Softapplication' AND DIMENSION = 'quality') AS cached
+ ON cached.cacheable_id=softapplications.id").
order("cached.avg").reverse_order
@contesting_softapplication = paginateHelper @contesting_softapplication, 10
-##引用base_newcontest整体样式
+ #引用base_newcontest整体样式
@contest = Contest.find_by_id(params[:id])
respond_to do |format|
format.html {
@@ -384,15 +449,15 @@ class ContestsController < ApplicationController
###end
-def show_notification
- @contest = Contest.find_by_id(params[:id])
- respond_to do |format|
- format.html {
- render :layout => 'base_newcontest'
- }
- format.api
- end
-end
+ def show_notification
+ @contest = Contest.find_by_id(params[:id])
+ respond_to do |format|
+ format.html {
+ render :layout => 'base_newcontest'
+ }
+ format.api
+ end
+ end
def set_reward_project
@@ -437,7 +502,11 @@ end
project = Project.find(params[:contest])
contest_message = params[:contest_for_save][:contest_message]
if ContestingProject.where("project_id = ? and contest_id = ?", project.id, @contest.id).size == 0
- if ContestingProject.cerate_contesting(@contest.id, project.id, contest_message)
+ # modified by longjun, create 写错了
+ # if ContestingProject.cerate_contesting(@contest.id, project.id, contest_message)
+ if ContestingProject.create_contesting(@contest.id, project.id, contest_message)
+ # end longjun
+
flash.now[:notice] = l(:label_bidding_contest_succeed)
end
else
@@ -633,9 +702,7 @@ end
end
- def manage
- end
private
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
index e1af9ca34..63ac3baaa 100644
--- a/app/controllers/messages_controller.rb
+++ b/app/controllers/messages_controller.rb
@@ -1,202 +1,202 @@
-# 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 MessagesController < ApplicationController
- include ApplicationHelper
- menu_item :boards
- default_search_scope :messages
- before_filter :find_board, :only => [:new, :preview,:edit]
- before_filter :find_attachments, :only => [:preview]
- before_filter :find_message, :except => [:new, :preview]
- before_filter :authorize, :except => [:preview, :edit, :destroy, :new]
-
- helper :boards
- helper :watchers
- helper :attachments
- include AttachmentsHelper
- helper :project_score
-
- REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
-
- # Show a topic and its replies
- def show
- @isReply = true
- page = params[:page]
- # Find the page of the requested reply
- if params[:r] && page.nil?
- offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
- page = 1 + offset / REPLIES_PER_PAGE
- end
-
- @reply_count = @topic.children.count
- @reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
- @replies = @topic.children.
- includes(:author, :attachments, {:board => :project}).
- reorder("#{Message.table_name}.created_on DESC").
- limit(@reply_pages.per_page).
- offset(@reply_pages.offset).
- all
-
- @reply = Message.new(:subject => "RE: #{@message.subject}")
- if @course
- render :action => "show", :layout => "base_courses"#by young
- else
- render :action => "show", :layout => "base_projects"#by young
- end
- end
-
- # Create a new topic
- def new
- @message = Message.new
- @message.author = User.current
- @message.board = @board
- @message.safe_attributes = params[:message]
- if request.post?
- @message.save_attachments(params[:attachments])
- if @message.save
- call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
- render_attachment_warning_if_needed(@message)
- redirect_to board_message_path(@board, @message)
- else
- layout_file = @project ? 'base_projects' : 'base_courses'
- render :action => 'new', :layout => layout_file
- end
- end
- end
-
- # Reply to a topic
- def reply
- if params[:reply][:content] == ""
- (redirect_to board_message_path(@board, @topic, :r => @reply), :notice => l(:label_reply_empty);return)
- end
- @quote = params[:quote][:quote]
- @reply = Message.new
- @reply.author = User.current
- @reply.board = @board
- @reply.safe_attributes = params[:reply]
- @reply.content = @quote + @reply.content
- @topic.children << @reply
- #@topic.update_attribute(:updated_on, Time.now)
- if !@reply.new_record?
- call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
- attachments = Attachment.attach_files(@reply, params[:attachments])
- render_attachment_warning_if_needed(@reply)
- else
- #render file: 'messages#show', layout: 'base_courses'
- end
- redirect_to board_message_path(@board, @topic, :r => @reply)
-
- end
-
- # Edit a message
- def edit
- @isReply = false
- if @project
- (render_403; return false) unless @message.editable_by?(User.current)
- else
- (render_403; return false) unless @message.course_editable_by?(User.current)
- end
- @message.safe_attributes = params[:message]
- if request.post? && @message.save
- attachments = Attachment.attach_files(@message, params[:attachments])
- render_attachment_warning_if_needed(@message)
- flash[:notice] = l(:notice_successful_update)
- @message.reload
- redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
- elsif request.get?
- respond_to do |format|
- format.html {
- layout_file = @project ? 'base_projects' : 'base_courses'
- render :layout => layout_file
- }
- end
- end
- end
-
- # Delete a messages
- def destroy
- if @project
- (render_403; return false) unless @message.destroyable_by?(User.current)
- else
- (render_403; return false) unless @message.course_destroyable_by?(User.current)
- end
- r = @message.to_param
- @message.destroy
- # modify by nwb
- if @project
- if @message.parent
- redirect_to board_message_path(@board, @message.parent, :r => r)
- else
- redirect_to project_board_path(@project, @board)
- end
- elsif @course
- if @message.parent
- redirect_to board_message_path(@board, @message.parent, :r => r)
- else
- redirect_to course_board_path(@course, @board)
- end
- end
- end
-
- def quote
- @subject = @message.subject
- @subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
-
- @content = "> #{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
- @temp = Message.new
- #@temp.content = "> #{ll(Setting.default_language, :text_user_wrote, @message.author)}> "
- @content << @message.content.to_s.strip.gsub(%r{
((.|\s)*?) }m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
- @content_html = textilizable(@content)
- @temp.content = @content_html
- #@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)} "
- #@content << @message.content.to_s.strip.gsub(%r{((.|\s)*?) }m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n") + "\n\n "
- #@content = "" << @content
- #@temp = Message.new
- #@temp.content = @content
-
- end
-
- def preview
- message = @board.messages.find_by_id(params[:id])
- @text = (params[:message] || params[:reply])[:content]
- @previewed = message
- render :partial => 'common/preview'
- end
-
-private
- def find_message
- return unless find_board
- @message = @board.messages.find(params[:id], :include => :parent)
- @topic = @message.root
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- def find_board
- #modify by nwb
- @board = Board.find(params[:board_id])
- if @board.project_id != -1 && @board.project_id != nil
- @project = @board.project
- elsif @board.course_id
- @course = @board.course
- end
-
- rescue ActiveRecord::RecordNotFound
- render_404
- nil
- 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 MessagesController < ApplicationController
+ include ApplicationHelper
+ menu_item :boards
+ default_search_scope :messages
+ before_filter :find_board, :only => [:new, :preview,:edit]
+ before_filter :find_attachments, :only => [:preview]
+ before_filter :find_message, :except => [:new, :preview]
+ before_filter :authorize, :except => [:preview, :edit, :destroy, :new]
+
+ helper :boards
+ helper :watchers
+ helper :attachments
+ include AttachmentsHelper
+ helper :project_score
+
+ REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE)
+
+ # Show a topic and its replies
+ def show
+ @isReply = true
+ page = params[:page]
+ # Find the page of the requested reply
+ if params[:r] && page.nil?
+ offset = @topic.children.count(:conditions => ["#{Message.table_name}.id < ?", params[:r].to_i])
+ page = 1 + offset / REPLIES_PER_PAGE
+ end
+
+ @reply_count = @topic.children.count
+ @reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
+ @replies = @topic.children.
+ includes(:author, :attachments, {:board => :project}).
+ reorder("#{Message.table_name}.created_on DESC").
+ limit(@reply_pages.per_page).
+ offset(@reply_pages.offset).
+ all
+
+ @reply = Message.new(:subject => "RE: #{@message.subject}")
+ if @course
+ render :action => "show", :layout => "base_courses"#by young
+ else
+ render :action => "show", :layout => "base_projects"#by young
+ end
+ end
+
+ # Create a new topic
+ def new
+ @message = Message.new
+ @message.author = User.current
+ @message.board = @board
+ @message.safe_attributes = params[:message]
+ if request.post?
+ @message.save_attachments(params[:attachments])
+ if @message.save
+ call_hook(:controller_messages_new_after_save, { :params => params, :message => @message})
+ render_attachment_warning_if_needed(@message)
+ redirect_to board_message_path(@board, @message)
+ else
+ layout_file = @project ? 'base_projects' : 'base_courses'
+ render :action => 'new', :layout => layout_file
+ end
+ end
+ end
+
+ # Reply to a topic
+ def reply
+ if params[:reply][:content] == ""
+ (redirect_to board_message_path(@board, @topic, :r => @reply), :notice => l(:label_reply_empty);return)
+ end
+ @quote = params[:quote][:quote]
+ @reply = Message.new
+ @reply.author = User.current
+ @reply.board = @board
+ @reply.safe_attributes = params[:reply]
+ @reply.content = @quote + @reply.content
+ @topic.children << @reply
+ #@topic.update_attribute(:updated_on, Time.now)
+ if !@reply.new_record?
+ call_hook(:controller_messages_reply_after_save, { :params => params, :message => @reply})
+ attachments = Attachment.attach_files(@reply, params[:attachments])
+ render_attachment_warning_if_needed(@reply)
+ else
+ #render file: 'messages#show', layout: 'base_courses'
+ end
+ redirect_to board_message_path(@board, @topic, :r => @reply)
+
+ end
+
+ # Edit a message
+ def edit
+ @isReply = false
+ if @project
+ (render_403; return false) unless @message.editable_by?(User.current)
+ else
+ (render_403; return false) unless @message.course_editable_by?(User.current)
+ end
+ @message.safe_attributes = params[:message]
+ if request.post? && @message.save
+ attachments = Attachment.attach_files(@message, params[:attachments])
+ render_attachment_warning_if_needed(@message)
+ flash[:notice] = l(:notice_successful_update)
+ @message.reload
+ redirect_to board_message_path(@message.board, @message.root, :r => (@message.parent_id && @message.id))
+ elsif request.get?
+ respond_to do |format|
+ format.html {
+ layout_file = @project ? 'base_projects' : 'base_courses'
+ render :layout => layout_file
+ }
+ end
+ end
+ end
+
+ # Delete a messages
+ def destroy
+ if @project
+ (render_403; return false) unless @message.destroyable_by?(User.current)
+ else
+ (render_403; return false) unless @message.course_destroyable_by?(User.current)
+ end
+ r = @message.to_param
+ @message.destroy
+ # modify by nwb
+ if @project
+ if @message.parent
+ redirect_to board_message_path(@board, @message.parent, :r => r)
+ else
+ redirect_to project_board_path(@project, @board)
+ end
+ elsif @course
+ if @message.parent
+ redirect_to board_message_path(@board, @message.parent, :r => r)
+ else
+ redirect_to course_board_path(@course, @board)
+ end
+ end
+ end
+
+ def quote
+ @subject = @message.subject
+ @subject = "RE: #{@subject}" unless @subject.starts_with?('RE:')
+
+ @content = "> #{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> "
+ @temp = Message.new
+ #@temp.content = "> #{ll(Setting.default_language, :text_user_wrote, @message.author)}> "
+ @content << @message.content.to_s.strip.gsub(%r{((.|\s)*?) }m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
+ @content_html = textilizable(@content)
+ @temp.content = @content_html
+ #@content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)} "
+ #@content << @message.content.to_s.strip.gsub(%r{((.|\s)*?) }m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n") + " \n\n "
+ #@content = "" << @content
+ #@temp = Message.new
+ #@temp.content = @content
+
+ end
+
+ def preview
+ message = @board.messages.find_by_id(params[:id])
+ @text = (params[:message] || params[:reply])[:content]
+ @previewed = message
+ render :partial => 'common/preview'
+ end
+
+private
+ def find_message
+ return unless find_board
+ @message = @board.messages.find(params[:id], :include => :parent)
+ @topic = @message.root
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ def find_board
+ #modify by nwb
+ @board = Board.find(params[:board_id])
+ if @board.project_id != -1 && @board.project_id != nil
+ @project = @board.project
+ elsif @board.course_id
+ @course = @board.course
+ end
+
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ nil
+ end
+end
diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb
index 32ae2efc2..1e58c53f1 100644
--- a/app/controllers/my_controller.rb
+++ b/app/controllers/my_controller.rb
@@ -75,6 +75,7 @@ class MyController < ApplicationController
# Edit user's account
def account
@user = User.current
+ lg=@user.login
@pref = @user.pref
diskfile = disk_filename('User', @user.id)
diskfile1 = diskfile + 'temp'
@@ -137,7 +138,7 @@ class MyController < ApplicationController
if File.exist?(diskfile1)
File.delete(diskfile1)
end
- @user
+ @user.login = lg
end
else
# 确保文件被删除
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index a705d6934..39865d4e9 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -1,606 +1,606 @@
-# Redmine - project management software
-# Copyright (C) 2006-2013 Jean-Philippe Lang
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-require 'SVG/Graph/Bar'
-require 'SVG/Graph/BarHorizontal'
-require 'digest/sha1'
-require 'redmine/scm/adapters/abstract_adapter'
-require 'tempfile'
-
-class ChangesetNotFound < Exception; end
-class InvalidRevisionParam < Exception; end
-
-class RepositoriesController < ApplicationController
- menu_item :repository
- menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
- default_search_scope :changesets
-
- before_filter :find_project_by_project_id, :only => [:new, :create, :newrepo]
- before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
- before_filter :find_project_repository, :except => [:new, :create, :newcreate, :edit, :update, :destroy, :committers, :newrepo]
- before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
- before_filter :authorize , :except => [:newrepo,:newcreate,:fork]
- accept_rss_auth :revisions
- # hidden repositories filter // 隐藏代码过滤器
- before_filter :check_hidden_repo, :only => [:show, :stats, :revisions, :revision, :diff ]
- helper :repositories
- include RepositoriesHelper
- helper :project_score
- #@root_path = RepositoriesHelper::ROOT_PATH
-
-
- rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
- def new
- scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
- @repository = Repository.factory(scm)
- @repository.is_default = @project.repository.nil?
- @repository.project = @project
- @course_tag = params[:course]
- if @course_tag == 1
- render :layout => 'base_courses'
- else
- render :layout => 'base_projects'
- end
- end
-
-
- def newrepo
- scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
- @repository = Repository.factory(scm)
- @repository.is_default = @project.repository.nil?
- @repository.project = @project
- @course_tag = params[:course]
- if @course_tag == 1
- render :layout => 'base_courses'
- else
- render :layout => 'base_projects'
- end
- end
-
- def fork
- @repository_url = params[:repository_url]
-
- # @repository.url
- # system "htpasswd -mb "+@root_path+"user.passwd "+params[:repository][:identifier]+" "+@upasswd
- # system "echo -e '"+params[:project_id]+"-"+params[:repository][:identifier]+"-write:"+
- # " "+params[:repository][:identifier]+"' >> "+@root_path+"group.passwd"
- system "git clone --bare "+@repository_url
- # system "mv "+@project_path+"/hooks/post-update{.sample,}"
- # system "chmod a+x "+@project_path+"/hooks/post-update"
- # system "."+@project_path+"/hooks/post-update"
- # system "echo -e 'Allow from all \n Order Deny,Allow \n "+
- # " \n"+
- # "Require group "+params[:project_id]+"-"+params[:repository][:identifier]+"-write \n "+
- # " \n ' >>"+
- # @project_path+"/.htaccess"
- flash[:notice] = l(:label_notice_fork_successed)
- @repositories = @project.repositories
- render :action => 'show', :layout => 'base_projects'
- end
-
- def create
- if params[:repository_scm].to_s == 'Gitlab'
- # add by nwb
- # 增加对gitlab版本库的支持
- attrs = pickup_extra_info
- @repository = Repository.factory('Git')
- @repository.safe_attributes = params[:repository]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
- if request.post? && @repository.save
- redirect_to settings_project_path(@project, :tab => 'repositories')
- else
- render :action => 'new'
- end
- else # 原逻辑
- ##xianbo
- @root_path=RepositoriesHelper::ROOT_PATH
- @repository_name=User.current.login.to_s+"/"+params[:repository][:identifier]+".git"
- @project_path=@root_path+"htdocs/"+@repository_name
- @repository_tag=params[:repository][:upassword]
- @repo_name=User.current.login.to_s+"_"+params[:repository][:identifier]
- logger.info "htpasswd -mb "+@root_path+"user.passwd "+@repo_name+": "+@repository_tag
- logger.info "the value of create repository"+@root_path+": "+@repository_name+": "+@project_path+": "+@repo_name
- attrs = pickup_extra_info
- if((@repository_tag!="")&¶ms[:repository_scm]=="Git")
- params[:repository][:url]=@project_path
- end
- ###xianbo
- @repository = Repository.factory(params[:repository_scm])
- @repository.safe_attributes = params[:repository]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- #by xianbo
-
- @repository.project = @project
- if request.post? && @repository.save
- if(params[:repository_scm]=="Git")
- system "htpasswd -mb "+@root_path+"user.passwd "+@repo_name+" "+@repository_tag
- system "echo -e '"+@repo_name+"-write:"+
- " "+@repo_name+"' >> "+@root_path+"group.passwd"
- system "mkdir "+@root_path+"htdocs/"+User.current.login.to_s
- system "git init --bare "+@project_path
- system "mv "+@project_path+"/hooks/post-update{.sample,}"
- system "chmod a+x "+@project_path+"/hooks/post-update"
- system "echo -e 'Allow from all \n Order Deny,Allow \n "+
- " \n"+
- "Require group "+@repo_name+"-write \n "+
- " \n ' >> "+
- @root_path+"htdocs/"+ @repository_name+"/.htaccess"
- system "cd "+@project_path+" ;git update-server-info"
- # if(create_repo_file&&create_passwd&&create_group&&init_repository&&add_privilege&&init_server_info)
- # else
- # logger.info "An error occured when authenticating "+"create passwd"+@creat_passwd+"create_group"+
- # crate_group+"create repository file "+create_repo_file+"init repository"+init_repostory+
- # "aad privilege to rpository"+add_privilege+"init server infos"+init_server_info
- # end
- @repository.update_attributes(:login => User.current.login.to_s)
- end
- redirect_to settings_project_path(@project, :tab => 'repositories')
- else if(@repository_tag)
- render :action => 'newrepo', :layout =>'base_projects'
- else
- render :action => 'new', :layout =>'base_projects'
- end
- end
- end
- end
-
- def edit
- end
-
- def update
- attrs = pickup_extra_info
- @repository.safe_attributes = attrs[:attrs]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
- if request.put? && @repository.save
- redirect_to settings_project_path(@project, :tab => 'repositories')
- else
- render :action => 'edit'
- end
- end
-
- def pickup_extra_info
- p = {}
- p_extra = {}
- params[:repository].each do |k, v|
- if k =~ /^extra_/
- p_extra[k] = v
- else
- p[k] = v
- end
- end
- {:attrs => p, :attrs_extra => p_extra}
- end
- private :pickup_extra_info
-
- def committers
- @committers = @repository.committers
- @users = @project.users
- additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
- @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
- @users.compact!
- @users.sort!
- if request.post? && params[:committers].is_a?(Hash)
- # Build a hash with repository usernames as keys and corresponding user ids as values
- @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
- flash[:notice] = l(:notice_successful_update)
- redirect_to settings_project_path(@project, :tab => 'repositories')
- elsif request.get?
- respond_to do |format|
- format.html{
- render :layout => "project_base"
- }
- end
-
-
- end
- end
-
- def destroy
- @root_path=RepositoriesHelper::ROOT_PATH
- @repo_name=User.current.login.to_s+"_"+@repository.identifier.to_s
- @repository_name=User.current.login.to_s+"/"+@repository.identifier.to_s+".git"
- @middle=User.current.login.to_s+"_"+@repository.identifier.to_s+"-write:"
- @repository.destroy if request.delete?
- redirect_to settings_project_path(@project, :tab => 'repositories')
- if(@repository.type=="Repository::Git")
- logger.info "destory the repository value"+"root path"+@root_path+"repo_name"+@repo_name+
- "repository_name"+@repository_name+"user group"+@middle
- system "sed -i /"+@repo_name+"/{d} "+@root_path+"user.passwd"
- system "sed -i /"+@middle+"/{d} "+@root_path+"group.passwd"
- system "rm -r "+@root_path+"htdocs/"+@repository_name
- # if(@sed_user&&@sed_group&&@remove)
- # else
- # logger.info "An error occured when destory the repository"+"delete form passwd: \n"+
- # @sed_user+"delete from group"+@sed_group+"delete from file"+@remove
- # end
- end
- end
-
- def show
- ## TODO: the below will move to filter, done.
- # if !User.current.member_of?(@project)
- # if @project.hidden_repo
- # render_403
- # return -1
- # end
- # end
- #if( !User.current.member_of?(@project) || @project.hidden_repo)
- @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
-
- @entries = @repository.entries(@path, @rev)
- @changeset = @repository.find_changeset_by_name(@rev)
-
- #@project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
- #@ip = RepositoriesHelper::REPO_IP_ADDRESS
-
- if request.xhr?
- @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
- else
- #Modified by young
- # (show_error_not_found; return) unless @entries
- @changesets = @repository.latest_changesets(@path, @rev)
- @properties = @repository.properties(@path, @rev)
- @repositories = @project.repositories
- @course_tag = params[:course]
- project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
- ip = RepositoriesHelper::REPO_IP_ADDRESS
- @repos_url = "http://"+@repository.login.to_s+"_"+@repository.identifier.to_s+"@"+ip.to_s+
- @repository.url.slice(project_path_cut, @repository.url.length).to_s
- if @course_tag == 1
- render :action => 'show', :layout => 'base_courses'
- else
- render :action => 'show', :layout => 'base_projects'
- end
- end
- end
-
- alias_method :browse, :show
-
- def changes
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
- @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
- @properties = @repository.properties(@path, @rev)
- @changeset = @repository.find_changeset_by_name(@rev)
- render :layout => 'base_projects'
- end
-
- def revisions
- @changeset_count = @repository.changesets.count
- @changeset_pages = Paginator.new @changeset_count,
- per_page_option,
- params['page']
- @changesets = @repository.changesets.
- limit(@changeset_pages.per_page).
- offset(@changeset_pages.offset).
- includes(:user, :repository, :parents).
- all
-
- respond_to do |format|
- format.html { render :layout => 'base_projects' }
- format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
- end
- end
-
- def raw
- entry_and_raw(true)
- end
-
- def entry
- entry_and_raw(false)
- end
-
- def entry_and_raw(is_raw)
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
-
- # If the entry is a dir, show the browser
- (show; return) if @entry.is_dir?
-
- @content = @repository.cat(@path, @rev)
- (show_error_not_found; return) unless @content
- if is_raw ||
- (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
- ! is_entry_text_data?(@content, @path)
- # Force the download
- send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
- send_type = Redmine::MimeType.of(@path)
- send_opt[:type] = send_type.to_s if send_type
- send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
- send_data @content, send_opt
- else
- # Prevent empty lines when displaying a file with Windows style eol
- # TODO: UTF-16
- # Is this needs? AttachmentsController reads file simply.
- @content.gsub!("\r\n", "\n")
- @changeset = @repository.find_changeset_by_name(@rev)
- end
- end
- private :entry_and_raw
-
- def is_entry_text_data?(ent, path)
- # UTF-16 contains "\x00".
- # It is very strict that file contains less than 30% of ascii symbols
- # in non Western Europe.
- return true if Redmine::MimeType.is_type?('text', path)
- # Ruby 1.8.6 has a bug of integer divisions.
- # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
- return false if ent.is_binary_data?
- true
- end
- private :is_entry_text_data?
-
- def annotate
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
-
- @annotate = @repository.scm.annotate(@path, @rev)
- if @annotate.nil? || @annotate.empty?
- (render_error l(:error_scm_annotate); return)
- end
- ann_buf_size = 0
- @annotate.lines.each do |buf|
- ann_buf_size += buf.size
- end
- if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
- (render_error l(:error_scm_annotate_big_text_file); return)
- end
- @changeset = @repository.find_changeset_by_name(@rev)
- end
-
- def revision
- respond_to do |format|
- format.html{render :layout => 'project_base'}
- format.js {render :layout => false}
- end
- end
-
- # Adds a related issue to a changeset
- # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
- def add_related_issue
- @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
- if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
- @issue = nil
- end
-
- if @issue
- @changeset.issues << @issue
- end
- end
-
- # Removes a related issue from a changeset
- # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
- def remove_related_issue
- @issue = Issue.visible.find_by_id(params[:issue_id])
- if @issue
- @changeset.issues.delete(@issue)
- end
- end
-
- def diff
- if params[:format] == 'diff'
- @diff = @repository.diff(@path, @rev, @rev_to)
- (show_error_not_found; return) unless @diff
- filename = "changeset_r#{@rev}"
- filename << "_r#{@rev_to}" if @rev_to
- send_data @diff.join, :filename => "#{filename}.diff",
- :type => 'text/x-patch',
- :disposition => 'attachment'
- else
- @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
- @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
-
- # Save diff type as user preference
- if User.current.logged? && @diff_type != User.current.pref[:diff_type]
- User.current.pref[:diff_type] = @diff_type
- User.current.preference.save
- end
- @cache_key = "repositories/diff/#{@repository.id}/" +
- Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
- unless read_fragment(@cache_key)
- @diff = @repository.diff(@path, @rev, @rev_to)
- unless @diff
- show_error_not_found
- return
- end
- end
-
- @changeset = @repository.find_changeset_by_name(@rev)
- @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
- @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
- end
- render :layout => 'base_projects'
- end
-
- def stats
- @project_id = params[:id]
- @repository_id = @repository.identifier
- render :layout => 'base_projects'
- end
-
- def graph
- data = nil
- case params[:graph]
- when "commits_per_month"
- data = graph_commits_per_month(@repository)
- when "commits_per_author"
- data = graph_commits_per_author(@repository)
- end
- if data
- headers["Content-Type"] = "image/svg+xml"
- send_data(data, :type => "image/svg+xml", :disposition => "inline")
- else
- render_404
- end
- end
-
- private
-
- def find_repository
- @repository = Repository.find(params[:id])
- @project = @repository.project
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
-
- def find_project_repository
- @project = Project.find(params[:id])
- if params[:repository_id].present?
- @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
- else
- @repository = @project.repository
- end
- (render_404; return false) unless @repository
- @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
- @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
- @rev_to = params[:rev_to]
-
- unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
- if @repository.branches.blank?
- raise InvalidRevisionParam
- end
- end
- rescue ActiveRecord::RecordNotFound
- render_404
- rescue InvalidRevisionParam
- show_error_not_found
- end
-
- def find_changeset
- if @rev.present?
- @changeset = @repository.find_changeset_by_name(@rev)
- end
- show_error_not_found unless @changeset
- end
-
- def show_error_not_found
- render_error :message => l(:error_scm_not_found), :status => 404
- end
-
- def show_error_forbidden
- render_error :status => 403
- end
-
- # Handler for Redmine::Scm::Adapters::CommandFailed exception
- def show_error_command_failed(exception)
- render_error l(:error_scm_command_failed, exception.message)
- end
-
- def graph_commits_per_month(repository)
- @date_to = Date.today
- @date_from = @date_to << 11
- @date_from = Date.civil(@date_from.year, @date_from.month, 1)
- commits_by_day = Changeset.count(
- :all, :group => :commit_date,
- :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- commits_by_month = [0] * 12
- commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
-
- changes_by_day = Change.count(
- :all, :group => :commit_date, :include => :changeset,
- :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- changes_by_month = [0] * 12
- changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
-
- fields = []
- 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
-
- graph = SVG::Graph::Bar.new(
- :height => 300,
- :width => 600,
- :fields => fields.reverse,
- :stack => :side,
- :scale_integers => true,
- :step_x_labels => 2,
- :show_data_values => false,
- :graph_title => l(:label_commits_per_month),
- :show_graph_title => true
- )
-
- graph.add_data(
- :data => commits_by_month[0..11].reverse,
- :title => l(:label_revision_plural)
- )
-
- graph.add_data(
- :data => changes_by_month[0..11].reverse,
- :title => l(:label_change_plural)
- )
-
- graph.burn
- end
-
- def graph_commits_per_author(repository)
- commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
- commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
-
- changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
- h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
-
- fields = commits_by_author.collect {|r| r.first}
- commits_data = commits_by_author.collect {|r| r.last}
- changes_data = commits_by_author.collect {|r| h[r.first] || 0}
-
- fields = fields + [""]*(10 - fields.length) if fields.length<10
- commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
- changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
-
- # Remove email adress in usernames
- fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
-
- graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
- :width => 600,
- :fields => fields,
- :stack => :side,
- :scale_integers => true,
- :show_data_values => false,
- :rotate_y_labels => false,
- :graph_title => l(:label_commits_per_author),
- :show_graph_title => true
- )
- graph.add_data(
- :data => commits_data,
- :title => l(:label_revision_plural)
- )
- graph.add_data(
- :data => changes_data,
- :title => l(:label_change_plural)
- )
- graph.burn
- end
- def check_hidden_repo
- project = Project.find(params[:id])
- if !User.current.member_of?(project)
- if project.hidden_repo
- #render_403
- end
- end
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-end
+# Redmine - project management software
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require 'SVG/Graph/Bar'
+require 'SVG/Graph/BarHorizontal'
+require 'digest/sha1'
+require 'redmine/scm/adapters/abstract_adapter'
+require 'tempfile'
+
+class ChangesetNotFound < Exception; end
+class InvalidRevisionParam < Exception; end
+
+class RepositoriesController < ApplicationController
+ menu_item :repository
+ menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
+ default_search_scope :changesets
+
+ before_filter :find_project_by_project_id, :only => [:new, :create, :newrepo]
+ before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
+ before_filter :find_project_repository, :except => [:new, :create, :newcreate, :edit, :update, :destroy, :committers, :newrepo]
+ before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
+ before_filter :authorize , :except => [:newrepo,:newcreate,:fork]
+ accept_rss_auth :revisions
+ # hidden repositories filter // 隐藏代码过滤器
+ before_filter :check_hidden_repo, :only => [:show, :stats, :revisions, :revision, :diff ]
+ helper :repositories
+ include RepositoriesHelper
+ helper :project_score
+ #@root_path = RepositoriesHelper::ROOT_PATH
+
+
+ rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
+ def new
+ scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
+ @repository = Repository.factory(scm)
+ @repository.is_default = @project.repository.nil?
+ @repository.project = @project
+ @course_tag = params[:course]
+ if @course_tag == 1
+ render :layout => 'base_courses'
+ else
+ render :layout => 'base_projects'
+ end
+ end
+
+
+ def newrepo
+ scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
+ @repository = Repository.factory(scm)
+ @repository.is_default = @project.repository.nil?
+ @repository.project = @project
+ @course_tag = params[:course]
+ if @course_tag == 1
+ render :layout => 'base_courses'
+ else
+ render :layout => 'base_projects'
+ end
+ end
+
+ def fork
+ @repository_url = params[:repository_url]
+
+ # @repository.url
+ # system "htpasswd -mb "+@root_path+"user.passwd "+params[:repository][:identifier]+" "+@upasswd
+ # system "echo -e '"+params[:project_id]+"-"+params[:repository][:identifier]+"-write:"+
+ # " "+params[:repository][:identifier]+"' >> "+@root_path+"group.passwd"
+ system "git clone --bare "+@repository_url
+ # system "mv "+@project_path+"/hooks/post-update{.sample,}"
+ # system "chmod a+x "+@project_path+"/hooks/post-update"
+ # system "."+@project_path+"/hooks/post-update"
+ # system "echo -e 'Allow from all \n Order Deny,Allow \n "+
+ # " \n"+
+ # "Require group "+params[:project_id]+"-"+params[:repository][:identifier]+"-write \n "+
+ # " \n ' >>"+
+ # @project_path+"/.htaccess"
+ flash[:notice] = l(:label_notice_fork_successed)
+ @repositories = @project.repositories
+ render :action => 'show', :layout => 'base_projects'
+ end
+
+ def create
+ if params[:repository_scm].to_s == 'Gitlab'
+ # add by nwb
+ # 增加对gitlab版本库的支持
+ attrs = pickup_extra_info
+ @repository = Repository.factory('Git')
+ @repository.safe_attributes = params[:repository]
+ if attrs[:attrs_extra].keys.any?
+ @repository.merge_extra_info(attrs[:attrs_extra])
+ end
+ @repository.project = @project
+ if request.post? && @repository.save
+ redirect_to settings_project_path(@project, :tab => 'repositories')
+ else
+ render :action => 'new'
+ end
+ else # 原逻辑
+ ##xianbo
+ @root_path=RepositoriesHelper::ROOT_PATH
+ @repository_name=User.current.login.to_s+"/"+params[:repository][:identifier]+".git"
+ @project_path=@root_path+"htdocs/"+@repository_name
+ @repository_tag=params[:repository][:upassword]
+ @repo_name=User.current.login.to_s+"_"+params[:repository][:identifier]
+ logger.info "htpasswd -mb "+@root_path+"user.passwd "+@repo_name+": "+@repository_tag
+ logger.info "the value of create repository"+@root_path+": "+@repository_name+": "+@project_path+": "+@repo_name
+ attrs = pickup_extra_info
+ if((@repository_tag!="")&¶ms[:repository_scm]=="Git")
+ params[:repository][:url]=@project_path
+ end
+ ###xianbo
+ @repository = Repository.factory(params[:repository_scm])
+ @repository.safe_attributes = params[:repository]
+ if attrs[:attrs_extra].keys.any?
+ @repository.merge_extra_info(attrs[:attrs_extra])
+ end
+ #by xianbo
+
+ @repository.project = @project
+ if request.post? && @repository.save
+ if(params[:repository_scm]=="Git")
+ system "htpasswd -mb "+@root_path+"user.passwd "+@repo_name+" "+@repository_tag
+ system "echo -e '"+@repo_name+"-write:"+
+ " "+@repo_name+"' >> "+@root_path+"group.passwd"
+ system "mkdir "+@root_path+"htdocs/"+User.current.login.to_s
+ system "git init --bare "+@project_path
+ system "mv "+@project_path+"/hooks/post-update{.sample,}"
+ system "chmod a+x "+@project_path+"/hooks/post-update"
+ system "echo -e 'Allow from all \n Order Deny,Allow \n "+
+ " \n"+
+ "Require group "+@repo_name+"-write \n "+
+ " \n ' >> "+
+ @root_path+"htdocs/"+ @repository_name+"/.htaccess"
+ system "cd "+@project_path+" ;git update-server-info"
+ # if(create_repo_file&&create_passwd&&create_group&&init_repository&&add_privilege&&init_server_info)
+ # else
+ # logger.info "An error occured when authenticating "+"create passwd"+@creat_passwd+"create_group"+
+ # crate_group+"create repository file "+create_repo_file+"init repository"+init_repostory+
+ # "aad privilege to rpository"+add_privilege+"init server infos"+init_server_info
+ # end
+ @repository.update_attributes(:login => User.current.login.to_s)
+ end
+ redirect_to settings_project_path(@project, :tab => 'repositories')
+ else if(@repository_tag)
+ render :action => 'newrepo', :layout =>'base_projects'
+ else
+ render :action => 'new', :layout =>'base_projects'
+ end
+ end
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ attrs = pickup_extra_info
+ @repository.safe_attributes = attrs[:attrs]
+ if attrs[:attrs_extra].keys.any?
+ @repository.merge_extra_info(attrs[:attrs_extra])
+ end
+ @repository.project = @project
+ if request.put? && @repository.save
+ redirect_to settings_project_path(@project, :tab => 'repositories')
+ else
+ render :action => 'edit'
+ end
+ end
+
+ def pickup_extra_info
+ p = {}
+ p_extra = {}
+ params[:repository].each do |k, v|
+ if k =~ /^extra_/
+ p_extra[k] = v
+ else
+ p[k] = v
+ end
+ end
+ {:attrs => p, :attrs_extra => p_extra}
+ end
+ private :pickup_extra_info
+
+ def committers
+ @committers = @repository.committers
+ @users = @project.users
+ additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
+ @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
+ @users.compact!
+ @users.sort!
+ if request.post? && params[:committers].is_a?(Hash)
+ # Build a hash with repository usernames as keys and corresponding user ids as values
+ @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
+ flash[:notice] = l(:notice_successful_update)
+ redirect_to settings_project_path(@project, :tab => 'repositories')
+ elsif request.get?
+ respond_to do |format|
+ format.html{
+ render :layout => "project_base"
+ }
+ end
+
+
+ end
+ end
+
+ def destroy
+ @root_path=RepositoriesHelper::ROOT_PATH
+ @repo_name=User.current.login.to_s+"_"+@repository.identifier.to_s
+ @repository_name=User.current.login.to_s+"/"+@repository.identifier.to_s+".git"
+ @middle=User.current.login.to_s+"_"+@repository.identifier.to_s+"-write:"
+ @repository.destroy if request.delete?
+ redirect_to settings_project_path(@project, :tab => 'repositories')
+ if(@repository.type=="Repository::Git")
+ logger.info "destory the repository value"+"root path"+@root_path+"repo_name"+@repo_name+
+ "repository_name"+@repository_name+"user group"+@middle
+ system "sed -i /"+@repo_name+"/{d} "+@root_path+"user.passwd"
+ system "sed -i /"+@middle+"/{d} "+@root_path+"group.passwd"
+ system "rm -r "+@root_path+"htdocs/"+@repository_name
+ # if(@sed_user&&@sed_group&&@remove)
+ # else
+ # logger.info "An error occured when destory the repository"+"delete form passwd: \n"+
+ # @sed_user+"delete from group"+@sed_group+"delete from file"+@remove
+ # end
+ end
+ end
+
+ def show
+ ## TODO: the below will move to filter, done.
+ # if !User.current.member_of?(@project)
+ # if @project.hidden_repo
+ # render_403
+ # return -1
+ # end
+ # end
+ #if( !User.current.member_of?(@project) || @project.hidden_repo)
+ @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
+
+ @entries = @repository.entries(@path, @rev)
+ @changeset = @repository.find_changeset_by_name(@rev)
+
+ #@project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
+ #@ip = RepositoriesHelper::REPO_IP_ADDRESS
+
+ if request.xhr?
+ @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
+ else
+ #Modified by young
+ # (show_error_not_found; return) unless @entries
+ @changesets = @repository.latest_changesets(@path, @rev)
+ @properties = @repository.properties(@path, @rev)
+ @repositories = @project.repositories
+ @course_tag = params[:course]
+ project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
+ ip = RepositoriesHelper::REPO_IP_ADDRESS
+ @repos_url = "http://"+@repository.login.to_s+"_"+@repository.identifier.to_s+"@"+ip.to_s+
+ @repository.url.slice(project_path_cut, @repository.url.length).to_s
+ if @course_tag == 1
+ render :action => 'show', :layout => 'base_courses'
+ else
+ render :action => 'show', :layout => 'base_projects'
+ end
+ end
+ end
+
+ alias_method :browse, :show
+
+ def changes
+ @entry = @repository.entry(@path, @rev)
+ (show_error_not_found; return) unless @entry
+ @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
+ @properties = @repository.properties(@path, @rev)
+ @changeset = @repository.find_changeset_by_name(@rev)
+ render :layout => 'base_projects'
+ end
+
+ def revisions
+ @changeset_count = @repository.changesets.count
+ @changeset_pages = Paginator.new @changeset_count,
+ per_page_option,
+ params['page']
+ @changesets = @repository.changesets.
+ limit(@changeset_pages.per_page).
+ offset(@changeset_pages.offset).
+ includes(:user, :repository, :parents).
+ all
+
+ respond_to do |format|
+ format.html { render :layout => 'base_projects' }
+ format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
+ end
+ end
+
+ def raw
+ entry_and_raw(true)
+ end
+
+ def entry
+ entry_and_raw(false)
+ end
+
+ def entry_and_raw(is_raw)
+ @entry = @repository.entry(@path, @rev)
+ (show_error_not_found; return) unless @entry
+
+ # If the entry is a dir, show the browser
+ (show; return) if @entry.is_dir?
+
+ @content = @repository.cat(@path, @rev)
+ (show_error_not_found; return) unless @content
+ if is_raw ||
+ (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
+ ! is_entry_text_data?(@content, @path)
+ # Force the download
+ send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
+ send_type = Redmine::MimeType.of(@path)
+ send_opt[:type] = send_type.to_s if send_type
+ send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
+ send_data @content, send_opt
+ else
+ # Prevent empty lines when displaying a file with Windows style eol
+ # TODO: UTF-16
+ # Is this needs? AttachmentsController reads file simply.
+ @content.gsub!("\r\n", "\n")
+ @changeset = @repository.find_changeset_by_name(@rev)
+ end
+ end
+ private :entry_and_raw
+
+ def is_entry_text_data?(ent, path)
+ # UTF-16 contains "\x00".
+ # It is very strict that file contains less than 30% of ascii symbols
+ # in non Western Europe.
+ return true if Redmine::MimeType.is_type?('text', path)
+ # Ruby 1.8.6 has a bug of integer divisions.
+ # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
+ return false if ent.is_binary_data?
+ true
+ end
+ private :is_entry_text_data?
+
+ def annotate
+ @entry = @repository.entry(@path, @rev)
+ (show_error_not_found; return) unless @entry
+
+ @annotate = @repository.scm.annotate(@path, @rev)
+ if @annotate.nil? || @annotate.empty?
+ (render_error l(:error_scm_annotate); return)
+ end
+ ann_buf_size = 0
+ @annotate.lines.each do |buf|
+ ann_buf_size += buf.size
+ end
+ if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
+ (render_error l(:error_scm_annotate_big_text_file); return)
+ end
+ @changeset = @repository.find_changeset_by_name(@rev)
+ end
+
+ def revision
+ respond_to do |format|
+ format.html{render :layout => 'project_base'}
+ format.js {render :layout => false}
+ end
+ end
+
+ # Adds a related issue to a changeset
+ # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
+ def add_related_issue
+ @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
+ if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
+ @issue = nil
+ end
+
+ if @issue
+ @changeset.issues << @issue
+ end
+ end
+
+ # Removes a related issue from a changeset
+ # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
+ def remove_related_issue
+ @issue = Issue.visible.find_by_id(params[:issue_id])
+ if @issue
+ @changeset.issues.delete(@issue)
+ end
+ end
+
+ def diff
+ if params[:format] == 'diff'
+ @diff = @repository.diff(@path, @rev, @rev_to)
+ (show_error_not_found; return) unless @diff
+ filename = "changeset_r#{@rev}"
+ filename << "_r#{@rev_to}" if @rev_to
+ send_data @diff.join, :filename => "#{filename}.diff",
+ :type => 'text/x-patch',
+ :disposition => 'attachment'
+ else
+ @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
+ @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
+
+ # Save diff type as user preference
+ if User.current.logged? && @diff_type != User.current.pref[:diff_type]
+ User.current.pref[:diff_type] = @diff_type
+ User.current.preference.save
+ end
+ @cache_key = "repositories/diff/#{@repository.id}/" +
+ Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
+ unless read_fragment(@cache_key)
+ @diff = @repository.diff(@path, @rev, @rev_to)
+ unless @diff
+ show_error_not_found
+ return
+ end
+ end
+
+ @changeset = @repository.find_changeset_by_name(@rev)
+ @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
+ @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
+ end
+ render :layout => 'base_projects'
+ end
+
+ def stats
+ @project_id = params[:id]
+ @repository_id = @repository.identifier
+ render :layout => 'base_projects'
+ end
+
+ def graph
+ data = nil
+ case params[:graph]
+ when "commits_per_month"
+ data = graph_commits_per_month(@repository)
+ when "commits_per_author"
+ data = graph_commits_per_author(@repository)
+ end
+ if data
+ headers["Content-Type"] = "image/svg+xml"
+ send_data(data, :type => "image/svg+xml", :disposition => "inline")
+ else
+ render_404
+ end
+ end
+
+ private
+
+ def find_repository
+ @repository = Repository.find(params[:id])
+ @project = @repository.project
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
+
+ def find_project_repository
+ @project = Project.find(params[:id])
+ if params[:repository_id].present?
+ @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
+ else
+ @repository = @project.repository
+ end
+ (render_404; return false) unless @repository
+ @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
+ @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
+ @rev_to = params[:rev_to]
+
+ unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
+ if @repository.branches.blank?
+ raise InvalidRevisionParam
+ end
+ end
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ rescue InvalidRevisionParam
+ show_error_not_found
+ end
+
+ def find_changeset
+ if @rev.present?
+ @changeset = @repository.find_changeset_by_name(@rev)
+ end
+ show_error_not_found unless @changeset
+ end
+
+ def show_error_not_found
+ render_error :message => l(:error_scm_not_found), :status => 404
+ end
+
+ def show_error_forbidden
+ render_error :status => 403
+ end
+
+ # Handler for Redmine::Scm::Adapters::CommandFailed exception
+ def show_error_command_failed(exception)
+ render_error l(:error_scm_command_failed, exception.message)
+ end
+
+ def graph_commits_per_month(repository)
+ @date_to = Date.today
+ @date_from = @date_to << 11
+ @date_from = Date.civil(@date_from.year, @date_from.month, 1)
+ commits_by_day = Changeset.count(
+ :all, :group => :commit_date,
+ :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
+ commits_by_month = [0] * 12
+ commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
+
+ changes_by_day = Change.count(
+ :all, :group => :commit_date, :include => :changeset,
+ :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
+ changes_by_month = [0] * 12
+ changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
+
+ fields = []
+ 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
+
+ graph = SVG::Graph::Bar.new(
+ :height => 300,
+ :width => 600,
+ :fields => fields.reverse,
+ :stack => :side,
+ :scale_integers => true,
+ :step_x_labels => 2,
+ :show_data_values => false,
+ :graph_title => l(:label_commits_per_month),
+ :show_graph_title => true
+ )
+
+ graph.add_data(
+ :data => commits_by_month[0..11].reverse,
+ :title => l(:label_revision_plural)
+ )
+
+ graph.add_data(
+ :data => changes_by_month[0..11].reverse,
+ :title => l(:label_change_plural)
+ )
+
+ graph.burn
+ end
+
+ def graph_commits_per_author(repository)
+ commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
+ commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
+
+ changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
+ h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
+
+ fields = commits_by_author.collect {|r| r.first}
+ commits_data = commits_by_author.collect {|r| r.last}
+ changes_data = commits_by_author.collect {|r| h[r.first] || 0}
+
+ fields = fields + [""]*(10 - fields.length) if fields.length<10
+ commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
+ changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
+
+ # Remove email adress in usernames
+ fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
+
+ graph = SVG::Graph::BarHorizontal.new(
+ :height => 400,
+ :width => 600,
+ :fields => fields,
+ :stack => :side,
+ :scale_integers => true,
+ :show_data_values => false,
+ :rotate_y_labels => false,
+ :graph_title => l(:label_commits_per_author),
+ :show_graph_title => true
+ )
+ graph.add_data(
+ :data => commits_data,
+ :title => l(:label_revision_plural)
+ )
+ graph.add_data(
+ :data => changes_data,
+ :title => l(:label_change_plural)
+ )
+ graph.burn
+ end
+ def check_hidden_repo
+ project = Project.find(params[:id])
+ if !User.current.member_of?(project)
+ if project.hidden_repo
+ #render_403
+ end
+ end
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index d479b8804..d8ecb4ef7 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,822 +1,822 @@
-# 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 UsersController < ApplicationController
- layout :setting_layout
- #Added by young
- menu_item :activity
- menu_item :user_information, :only => :info
- menu_item :user_course, :only => :user_courses
- menu_item :user_homework, :only => :user_homeworks
- menu_item :user_project, :only => [:user_projects, :watch_projects]
- menu_item :requirement_focus, :only => :watch_bids
- menu_item :requirement_focus, :only => :watch_contests
- menu_item :user_newfeedback, :only => :user_newfeedback
-
-
- #Ended by young
-
- before_filter :can_show_course, :only => [:user_courses,:user_homeworks]
- before_filter :require_admin, :except => [:show, :index, :search, :tag_save, :tag_saveEx,:user_projects, :user_newfeedback, :user_comments, :watch_bids, :watch_contests, :info,
- :user_watchlist, :user_fanslist,:update, :user_courses, :user_homeworks, :watch_projects, :show_score, :topic_score_index, :project_score_index,
- :activity_score_index, :influence_score_index, :score_index,:show_new_score, :topic_new_score_index, :project_new_score_index,
- :activity_new_score_index, :influence_new_score_index, :score_new_index,:update_score,:user_activities]
- #edit has been deleted by huang, 2013-9-23
- before_filter :find_user, :only => [:user_fanslist, :user_watchlist, :show, :edit, :update, :destroy, :edit_membership, :user_courses,
- :user_homeworks, :destroy_membership, :user_activities, :user_projects, :user_newfeedback, :user_comments,
- :watch_bids, :watch_contests, :info, :watch_projects, :show_score, :topic_score_index, :project_score_index,
- :activity_score_index, :influence_score_index, :score_index,:show_new_score, :topic_new_score_index, :project_new_score_index,
- :activity_new_score_index, :influence_new_score_index, :score_new_index]
- before_filter :auth_user_extension, only: :show
- before_filter :rest_user_score, only: :show
- accept_api_auth :index, :show, :create, :update, :destroy,:tag_save , :tag_saveEx
-
- #william
- before_filter :require_login, :only => [:tag_save,:tag_saveEx]
- #before_filter :refresh_changests, :only =>[:user_activities,:user_courses,:user_projects,:user_newfeedback]
-
-
- helper :sort
- include SortHelper
- helper :custom_fields
- include CustomFieldsHelper
- include AvatarHelper
- include WordsHelper
- include GitlabHelper
- include UserScoreHelper
- helper :user_score
-
- # added by liuping 关注
-
- helper :watchers
- helper :activities
-
- ### added by william
- include ActsAsTaggableOn::TagsHelper
-
- # fq
- helper :words
-
- def refresh_changests
- if !(@user.nil?) && !(@user.memberships.nil?)
- @user.memberships.each do |member|
- unless member.project.nil?
- member.project.repository.fetch_changesets if Setting.autofetch_changesets?
- end
- end
- end
- end
-
- #added by young
- def user_projects
-
- if User.current.admin?
- @memberships = @user.memberships.all(conditions: "projects.project_type = #{Project::ProjectType_project}")
- else
- cond = Project.visible_condition(User.current) + " AND projects.project_type <> 1"
- @memberships = @user.memberships.all(:conditions => cond)
- end
- #events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 20)
- #@events_by_day = events.group_by(&:event_date)
- @state = 0
-
-
- #add by huang
- unless User.current.admin?
- if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
- render_404
- return
- end
- end
- #end
-
- respond_to do |format|
- format.html
- format.api
- end
- end
-
-# added by bai
- def show_score
-
- end
-
- def show_new_score
- render :layout => false
- end
-# end
-
- ##added by fq
- def watch_bids
- cond = 'bids.reward_type <> 1'
- @bids = Bid.watched_by(@user).where('reward_type = ?', 1) # added by huang
- @offset, @limit = api_offset_and_limit({:limit => 10})
- @bid_count = @bids.count
- @bid_pages = Paginator.new @bid_count, @limit, params['page']
- @offset ||= @bid_pages.reverse_offset
- unless @offset == 0
- @bid = @bids.offset(@offset).limit(@limit).all.reverse
- else
- limit = @bid_count % @limit
- @bid = @bids.offset(@offset).limit(limit).all.reverse
- end
-
- respond_to do |format|
- format.html {
- render :layout => 'base_users'
- }
- format.api
- end
- end
-
-#new add by linchun
- def watch_contests
- @bids = Contest.watched_by(@user)
- @offset, @limit = api_offset_and_limit({:limit => 10})
- @contest_count = @contests.count
- @contest_pages = Paginator.new @contest_count, @limit, params['page']
- @offset ||= @contest_pages.reverse_offset
- unless @offset == 0
- @contest = @contests.offset(@offset).limit(@limit).all.reverse
- else
- limit = @bid_count % @limit
- @contest = @contests.offset(@offset).limit(limit).all.reverse
- end
-
- respond_to do |format|
- format.html {
- render :layout => 'base_users'
- }
- format.api
- end
- end
-
- # added by fq
- def user_activities
- redirect_to user_path(@user, type: params[:type], page: params[:page])
- return
- # useless abort.
- @watcher = User.watched_by_id(@user)
- events = []
- for user in @watcher
- events << Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 30)
- end
-
-
-
- @events_by_day = events.group_by(&:event_date)
-
- unless User.current.admin?
- if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
- render_404
- return
- end
- end
-
- respond_to do |format|
- format.html
- format.api
- end
- end
- # end
-
- # added by huang
- def user_homeworks
- @membership = @user.memberships.all(:conditions => Project.visible_condition(User.current))
- @memberships = []
- @membership.each do |membership|
- if membership.project.project_type == 1
- @memberships << membership
- end
- end
- @bid = []
- @memberships.each do |membership|
- @bid += membership.project.homeworks
- end
- @bid = @bid.group_by {|bid| bid.courses.first.id}
- unless User.current.admin?
- if !@user.active?
- render_404
- return
- end
- end
- end
-
-
- include CoursesHelper
- def user_courses
-
- unless User.current.admin?
- if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
- render_404
- return
- end
- end
-
- membership = @user.coursememberships.all#@user.coursememberships.all(:conditions => Course.visible_condition(User.current))
- membership.sort! {|older, newer| newer.created_on <=> older.created_on }
- @memberships = []
- membership.collect { |e|
- @memberships.push(e)
- }
- ## 判断课程是否过期 [需封装]
- @memberships_doing = []
- @memberships_done = []
- now_time = Time.now.year
- @memberships.map { |e|
- end_time = e.course.get_time.year
- isDone = course_endTime_timeout?(e.course)
- if isDone
- @memberships_done.push e
- else
- @memberships_doing.push e
- end
- }
- # respond_to do |format|
- # format.html
- # format.api
- # end
- end
-
-# modified by fq
- def user_newfeedback
- @jours = @user.journals_for_messages.where('m_parent_id IS NULL').order('created_on DESC')
- @jours.update_all(:is_readed => true, :status => false)
- @jours.each do |journal|
- fetch_user_leaveWord_reply(journal).update_all(:is_readed => true, :status => false)
- end
-
- @limit = 10
- @feedback_count = @jours.count
- @feedback_pages = Paginator.new @feedback_count, @limit, params['page']
- @offset ||= @feedback_pages.offset
- @jour = @jours[@offset, @limit]
- @state = false
- end
- # end
-
- def user_comments
-
- end
-
- #end
- def index
- @status = params[:status] || 1
- sort_init 'login', 'asc'
- sort_update %w(login firstname lastname mail admin created_on last_login_on)
-
- case params[:format]
- when 'xml', 'json'
- @offset, @limit = api_offset_and_limit({:limit => 15})
- else
- @limit = 15
- end
-
- # retrieve all users
- # 先内连一下statuses 保证排序之后数量一致
- scope = User.visible.
- joins("INNER JOIN user_statuses ON users.id = user_statuses.user_id")
-
- # unknow
- scope = scope.in_group(params[:group_id]) if params[:group_id].present?
-
- # pagination
- @user_count = scope.count
- @user_pages = Paginator.new @user_count, @limit, params['page']
-
- # users classify
- case params[:user_sort_type]
- when '0'
- # 创建时间排序
- @s_type = 0
- @users = scope.reorder('users.created_on DESC')
- when '1'
- # 活跃度排序, 就是所谓的得分情况
- @s_type = 1
- @users = scope.
- joins("LEFT JOIN option_numbers ON users.id = option_numbers.user_id and option_numbers.score_type = 1").
- reorder('option_numbers.total_score DESC')
- when '2'
- # 粉丝数排序
- @s_type = 2
- @users = scope.
- #joins("INNER JOIN user_statuses ON users.id = user_statuses.user_id").
- reorder('user_statuses.watchers_count DESC')
-
- else
- # 默认活跃度排序
- @s_type = 1
- @users = scope.
- joins("LEFT JOIN option_numbers ON users.id = option_numbers.user_id and option_numbers.score_type = 1").
- reorder('option_numbers.total_score DESC')
- end
-
- # limit and offset
- @users = @users.limit(@user_pages.per_page).offset(@user_pages.offset)
-
- @user_base_tag = params[:id] ? 'base_users':'users_base'
- respond_to do |format|
- format.html {
- @groups = Group.all.sort
- render :layout => @user_base_tag
- }
- format.api
- end
- end
-
- def search
- sort_init 'login', 'asc'
- sort_update %w(login firstname lastname mail admin created_on last_login_on)
- (redirect_to users_path, :notice => l(:label_sumbit_empty);return) if params[:name].blank?
- case params[:format]
- when 'xml', 'json'
- @offset, @limit = api_offset_and_limit({:limit => 15})
- else
- @limit = 15#per_page_option
- end
-
- @status = params[:status] || 1
- has = {
- "show_changesets" => true
- }
- scope = User.logged.status(@status)
- scope = scope.like(params[:name]) if params[:name].present?
- @user_count = scope.count
- @user_pages = Paginator.new @user_count, @limit, params['page']
- @user_base_tag = params[:id] ? 'base_users':'users_base'
- @offset ||= @user_pages.reverse_offset
- unless @offset == 0
- @users = scope.offset(@offset).limit(@limit).all.reverse
- else
- limit = @user_count % @limit
- if limit == 0
- limit = @limit
- end
- @users = scope.offset(@offset).limit(limit).all.reverse
- end
-
- respond_to do |format|
- format.html {
- @groups = Group.all.sort
- render :layout => @user_base_tag
- }
- format.api
- end
- end
-
- def show
- pre_count = 10 #limit
- case params[:type]
- when "1"
- if @user == User.current
- activity = Activity.where('user_id = ?', User.current.id).order('id desc')
- @activity_count = activity.count
- @activity_pages = Paginator.new @activity_count, pre_count, params['page']
- @activity = activity.offset(@activity_pages.offset).limit(@activity_pages.per_page).all
- @state = 1
- end
- when "2"
- message = []
- if @user == User.current
- message = JournalsForMessage.reference_message(@user.id)
- message += Journal.reference_message(@user.id)
- end
- @activity_count = message.size
- @info_pages = Paginator.new @activity_count, pre_count, params['page']
- messages = message.sort {|x,y| y.created_on <=> x.created_on }
- @message = messages[@info_pages.offset, @info_pages.per_page]
- @state = 2
- else
- where_condition = nil;
- # where_condition = "act_type <> 'JournalsForMessage'"
- if @user == User.current
- watcher = User.watched_by(@user)
- watcher.push(User.current)
- activity = Activity.where(where_condition).where('user_id in (?)', watcher).order('id desc')
- else
- activity = Activity.where(where_condition).where('user_id = ?', @user.id).order('id desc')
- end
- @activity_count = activity.count
- @activity_pages = Paginator.new @activity_count, pre_count, params['page']
- @activity = activity.offset(@activity_pages.offset).limit(@activity_pages.per_page).all
- @state = 0
- end
-
- if params[:user].present?
-
- user_temp = User.find_by_sql("select id from users where concat(lastname,firstname) like '%#{params[:user]}%' or lastname like '%#{params[:user]}%'")
-
- if user_temp.size > 1
- activity = Activity.where('user_id in (?)', user_temp).where('user_id in (?)', watcher).order('id desc')
- elsif user_temp.size == 1
- activity = Activity.where('user_id = ?', user_temp).where('user_id in (?)', watcher).order('id desc')
- else
- activity = Activity.where("1 = 0")
- end
- @offset, @limit = api_offset_and_limit({:limit => 10})
- @activity_count = activity.count
- @activity_pages = Paginator.new @activity_count, @limit, params['page']
- @offset ||= @activity_pages.offset
- @activity = activity.offset(@offset).limit(@limit)
- @state = 0
- end
-
-
-#Modified by nie
- unless User.current.admin?
- if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
- # redirect_to home_path
- render_404
- return
- end
- end
-
- respond_to do |format|
- format.html
- format.api
- end
- end
-
- ##end fq
-
- #### added by fq
- def info
-
- message = []
- if @user == User.current
- message = JournalsForMessage.reference_message(@user.id)
- message += Journal.reference_message(@user.id)
- end
- @offset, @limit = api_offset_and_limit({:limit => 10})
- @info_count = message.size
- @info_pages = Paginator.new @info_count, @limit, params['page']
- @offset ||= @info_pages.offset
-
- messages = message.sort {|x,y| y.created_on <=> x.created_on }
-
- @message = messages[@offset, @limit]
-
- unless User.current.admin?
- if !@user.active?
- render_404
- return
- end
- end
-
- respond_to do |format|
- format.html
- format.api
- end
- end
- #### end
-
-
- def new
- @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
- @auth_sources = AuthSource.all
- render :layout => "users_base"
- end
-
- def create
- @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
- @user.safe_attributes = params[:user]
- @user.admin = params[:user][:admin] || false
- @user.login = params[:user][:login]
- @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
-
- if @user.save
- @user.pref.attributes = params[:pref]
- @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
- @user.pref.save
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
-
- Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
-
- respond_to do |format|
- format.html {
- flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
- if params[:continue]
- redirect_to new_user_path
- else
- redirect_to edit_user_path(@user)
- end
- }
- format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
- end
- else
- @auth_sources = AuthSource.all
- # Clear password input
- @user.password = @user.password_confirmation = nil
-
- respond_to do |format|
- format.html { render :action => 'new',:layout => "users_base" }
- format.api { render_validation_errors(@user) }
- end
- end
-
- unless @user.id.nil?
- #后台注册的用户默认权限为男性开发员
- ue = UserExtensions.create(:identity => 3,
- :gender => 0,
- :user_id => @user.id)
- ue.save
- end
- end
-
- def edit
- @auth_sources = AuthSource.all
- @membership ||= Member.new
- end
-
- def watch_projects
- @watch_projects = Project.joins(:watchers).where("project_type <>? and watchable_type = ? and `watchers`.user_id = ?", '1','Project', @user.id)
- @state = 1
- respond_to do |format|
- format.html {
- render :layout => 'base_users'
- }
- format.api
- end
- end
-
- def update
- @user.admin = params[:user][:admin] if params[:user][:admin]
- @user.login = params[:user][:login] if params[:user][:login]
- if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
- @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
- end
- @user.safe_attributes = params[:user]
- # Was the account actived ? (do it before User#save clears the change)
- was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
- # TODO: Similar to My#account
- @user.pref.attributes = params[:pref]
- @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
-
- if @user.save
- @user.pref.save
- @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
-
- if was_activated
- Mailer.account_activated(@user).deliver
- elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
- Mailer.account_information(@user, params[:user][:password]).deliver
- end
-
- respond_to do |format|
- format.html {
- flash[:notice] = l(:notice_successful_update)
- redirect_to_referer_or edit_user_path(@user)
- }
- format.api { render_api_ok }
- end
- else
- @auth_sources = AuthSource.all
- @membership ||= Member.new
- # Clear password input
- @user.password = @user.password_confirmation = nil
-
- respond_to do |format|
- format.html { render :action => :edit }
- format.api { render_validation_errors(@user) }
- end
- end
- end
-
- def destroy
- @user.destroy
- respond_to do |format|
- format.html { redirect_back_or_default(admin_users_path) }
- format.api { render_api_ok }
- end
- end
-
- def edit_membership
- @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
- @membership.save
- respond_to do |format|
- format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
- format.js
- end
- end
-
- def destroy_membership
- @membership = Member.find(params[:membership_id])
- if @membership.deletable?
- @membership.destroy
- end
- respond_to do |format|
- format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
- format.js
- end
- end
-
- ################# added by william
- def tag_save
- @tags = params[:tag_for_save][:name]
- @obj_id = params[:tag_for_save][:object_id]
- @obj_flag = params[:tag_for_save][:object_flag]
-
- case @obj_flag
- when '1' then
- @obj = User.find_by_id(@obj_id)
- when '2' then
- @obj = Project.find_by_id(@obj_id)
- when '3' then
- @obj = Issue.find_by_id(@obj_id)
- when '4' then
- @obj = Bid.find_by_id(@obj_id)
- when '5' then
- @obj = Forum.find_by_id(@obj_id)
- when '6'
- @obj = Attachment.find_by_id(@obj_id)
- when '7' then
- @obj = Contest.find_by_id(@obj_id)
- when '8'
- @obj = OpenSourceProject.find_by_id(@obj_id)
- when '9'
- @obj = Course.find_by_id(@obj_id)
- else
- @obj = nil
- end
- unless @obj.nil?
- @obj.tag_list.add(@tags.split(","))
- else
- return
- end
- if @obj.save
- logger.debug "#{__FILE__}:#{__LINE__} ===> #{@obj.to_json}"
- else
- logger.error "#{__FILE__}:#{__LINE__} ===> #{@obj.errors.try(:full_messages)}"
- end
- respond_to do |format|
- format.js
- format.html
- end
- end
-
- def tag_saveEx
- @tags = params[:tag_name]
- @obj_id = params[:obj_id]
- @obj_flag = params[:obj_flag]
-
- case @obj_flag
- when '1' then
- @obj = User.find_by_id(@obj_id)
- when '2' then
- @obj = Project.find_by_id(@obj_id)
- when '3' then
- @obj = Issue.find_by_id(@obj_id)
- when '4' then
- @obj = Bid.find_by_id(@obj_id)
- when '5' then
- @obj = Forum.find_by_id(@obj_id)
- when '6'
- @obj = Attachment.find_by_id(@obj_id)
- when '7' then
- @obj = Contest.find_by_id(@obj_id)
- when '8'
- @obj = OpenSourceProject.find_by_id(@obj_id)
- when '9'
- @obj = Course.find_by_id(@obj_id)
- else
- @obj = nil
- end
- unless @obj.nil?
- @obj.tag_list.add(@tags.split(","))
- else
- return
- end
- if @obj.save
- ## 执行成功的操作。
- else
- #捕获异常
- end
- respond_to do |format|
- format.js
- format.html
- end
- end
- ###add by huang
- def user_watchlist
- end
- ###add by huang
- def user_fanslist
-
- end
-
- #william
- def update_extensions(user_extensions)
- user_extensions = params[:user_extensions]
- unless user_extensions.nil?
- user_extensions = UserExtensions.find_by_id(user_extensions.user_id)
-
- # user_extensions.
- end
- end
-
-# added by bai
- def topic_score_index
-
- end
-
- def project_score_index
-
- end
-
- def activity_score_index
-
- end
-
- def influence_score_index
-
- end
-
- def score_index
-
- end
-# end
- def topic_new_score_index
-
- end
-
- def project_new_score_index
-
- end
-
- def activity_new_score_index
-
- end
-
- def influence_new_score_index
-
- end
-
- def score_new_index
-
- end
-
- def update_score
- @user = User.find(params[:id])
- end
-
- private
-
- def find_user
- if params[:id] == 'current'
- require_login || return
- @user = User.current
- else
- @user = User.find(params[:id])
- end
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- def setting_layout(default_base='base_users')
- User.current.admin? ? default_base : default_base
- end
-
- # 必填自己的工作单位,其实就是学校
- def auth_user_extension
- if @user == User.current && (@user.user_extensions.nil? || @user.user_extensions.school.nil?)
- flash[:error] = l(:error_complete_occupation)
- redirect_to my_account_path
- end
- end
-
- #重置用户得分
- def rest_user_score
- memo_num(@user)
- messges_for_issue_num(@user)
- issues_status_num(@user)
- replay_for_memo_num(@user)
- tread_num(@user)
- praise_num(@user)
- changeset_num(@user)
- document_num(@user)
- attachment_num(@user)
- issue_done_ratio_num(@user)
- post_issue_num(@user)
- end
-
- #验证是否显示课程
- def can_show_course
- @first_page = FirstPage.where("page_type = 'project'").first
- if @first_page.show_course == 2
- render_404
- 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 UsersController < ApplicationController
+ layout :setting_layout
+ #Added by young
+ menu_item :activity
+ menu_item :user_information, :only => :info
+ menu_item :user_course, :only => :user_courses
+ menu_item :user_homework, :only => :user_homeworks
+ menu_item :user_project, :only => [:user_projects, :watch_projects]
+ menu_item :requirement_focus, :only => :watch_bids
+ menu_item :requirement_focus, :only => :watch_contests
+ menu_item :user_newfeedback, :only => :user_newfeedback
+
+
+ #Ended by young
+
+ before_filter :can_show_course, :only => [:user_courses,:user_homeworks]
+ before_filter :require_admin, :except => [:show, :index, :search, :tag_save, :tag_saveEx,:user_projects, :user_newfeedback, :user_comments, :watch_bids, :watch_contests, :info,
+ :user_watchlist, :user_fanslist,:update, :user_courses, :user_homeworks, :watch_projects, :show_score, :topic_score_index, :project_score_index,
+ :activity_score_index, :influence_score_index, :score_index,:show_new_score, :topic_new_score_index, :project_new_score_index,
+ :activity_new_score_index, :influence_new_score_index, :score_new_index,:update_score,:user_activities]
+ #edit has been deleted by huang, 2013-9-23
+ before_filter :find_user, :only => [:user_fanslist, :user_watchlist, :show, :edit, :update, :destroy, :edit_membership, :user_courses,
+ :user_homeworks, :destroy_membership, :user_activities, :user_projects, :user_newfeedback, :user_comments,
+ :watch_bids, :watch_contests, :info, :watch_projects, :show_score, :topic_score_index, :project_score_index,
+ :activity_score_index, :influence_score_index, :score_index,:show_new_score, :topic_new_score_index, :project_new_score_index,
+ :activity_new_score_index, :influence_new_score_index, :score_new_index]
+ before_filter :auth_user_extension, only: :show
+ before_filter :rest_user_score, only: :show
+ accept_api_auth :index, :show, :create, :update, :destroy,:tag_save , :tag_saveEx
+
+ #william
+ before_filter :require_login, :only => [:tag_save,:tag_saveEx]
+ #before_filter :refresh_changests, :only =>[:user_activities,:user_courses,:user_projects,:user_newfeedback]
+
+
+ helper :sort
+ include SortHelper
+ helper :custom_fields
+ include CustomFieldsHelper
+ include AvatarHelper
+ include WordsHelper
+ include GitlabHelper
+ include UserScoreHelper
+ helper :user_score
+
+ # added by liuping 关注
+
+ helper :watchers
+ helper :activities
+
+ ### added by william
+ include ActsAsTaggableOn::TagsHelper
+
+ # fq
+ helper :words
+
+ def refresh_changests
+ if !(@user.nil?) && !(@user.memberships.nil?)
+ @user.memberships.each do |member|
+ unless member.project.nil?
+ member.project.repository.fetch_changesets if Setting.autofetch_changesets?
+ end
+ end
+ end
+ end
+
+ #added by young
+ def user_projects
+
+ if User.current.admin?
+ @memberships = @user.memberships.all(conditions: "projects.project_type = #{Project::ProjectType_project}")
+ else
+ cond = Project.visible_condition(User.current) + " AND projects.project_type <> 1"
+ @memberships = @user.memberships.all(:conditions => cond)
+ end
+ #events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 20)
+ #@events_by_day = events.group_by(&:event_date)
+ @state = 0
+
+
+ #add by huang
+ unless User.current.admin?
+ if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
+ render_404
+ return
+ end
+ end
+ #end
+
+ respond_to do |format|
+ format.html
+ format.api
+ end
+ end
+
+# added by bai
+ def show_score
+
+ end
+
+ def show_new_score
+ render :layout => false
+ end
+# end
+
+ ##added by fq
+ def watch_bids
+ cond = 'bids.reward_type <> 1'
+ @bids = Bid.watched_by(@user).where('reward_type = ?', 1) # added by huang
+ @offset, @limit = api_offset_and_limit({:limit => 10})
+ @bid_count = @bids.count
+ @bid_pages = Paginator.new @bid_count, @limit, params['page']
+ @offset ||= @bid_pages.reverse_offset
+ unless @offset == 0
+ @bid = @bids.offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @bid_count % @limit
+ @bid = @bids.offset(@offset).limit(limit).all.reverse
+ end
+
+ respond_to do |format|
+ format.html {
+ render :layout => 'base_users'
+ }
+ format.api
+ end
+ end
+
+#new add by linchun
+ def watch_contests
+ @bids = Contest.watched_by(@user)
+ @offset, @limit = api_offset_and_limit({:limit => 10})
+ @contest_count = @contests.count
+ @contest_pages = Paginator.new @contest_count, @limit, params['page']
+ @offset ||= @contest_pages.reverse_offset
+ unless @offset == 0
+ @contest = @contests.offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @bid_count % @limit
+ @contest = @contests.offset(@offset).limit(limit).all.reverse
+ end
+
+ respond_to do |format|
+ format.html {
+ render :layout => 'base_users'
+ }
+ format.api
+ end
+ end
+
+ # added by fq
+ def user_activities
+ redirect_to user_path(@user, type: params[:type], page: params[:page])
+ return
+ # useless abort.
+ @watcher = User.watched_by_id(@user)
+ events = []
+ for user in @watcher
+ events << Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 30)
+ end
+
+
+
+ @events_by_day = events.group_by(&:event_date)
+
+ unless User.current.admin?
+ if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
+ render_404
+ return
+ end
+ end
+
+ respond_to do |format|
+ format.html
+ format.api
+ end
+ end
+ # end
+
+ # added by huang
+ def user_homeworks
+ @membership = @user.memberships.all(:conditions => Project.visible_condition(User.current))
+ @memberships = []
+ @membership.each do |membership|
+ if membership.project.project_type == 1
+ @memberships << membership
+ end
+ end
+ @bid = []
+ @memberships.each do |membership|
+ @bid += membership.project.homeworks
+ end
+ @bid = @bid.group_by {|bid| bid.courses.first.id}
+ unless User.current.admin?
+ if !@user.active?
+ render_404
+ return
+ end
+ end
+ end
+
+
+ include CoursesHelper
+ def user_courses
+
+ unless User.current.admin?
+ if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
+ render_404
+ return
+ end
+ end
+
+ membership = @user.coursememberships.all#@user.coursememberships.all(:conditions => Course.visible_condition(User.current))
+ membership.sort! {|older, newer| newer.created_on <=> older.created_on }
+ @memberships = []
+ membership.collect { |e|
+ @memberships.push(e)
+ }
+ ## 判断课程是否过期 [需封装]
+ @memberships_doing = []
+ @memberships_done = []
+ now_time = Time.now.year
+ @memberships.map { |e|
+ end_time = e.course.get_time.year
+ isDone = course_endTime_timeout?(e.course)
+ if isDone
+ @memberships_done.push e
+ else
+ @memberships_doing.push e
+ end
+ }
+ # respond_to do |format|
+ # format.html
+ # format.api
+ # end
+ end
+
+# modified by fq
+ def user_newfeedback
+ @jours = @user.journals_for_messages.where('m_parent_id IS NULL').order('created_on DESC')
+ @jours.update_all(:is_readed => true, :status => false)
+ @jours.each do |journal|
+ fetch_user_leaveWord_reply(journal).update_all(:is_readed => true, :status => false)
+ end
+
+ @limit = 10
+ @feedback_count = @jours.count
+ @feedback_pages = Paginator.new @feedback_count, @limit, params['page']
+ @offset ||= @feedback_pages.offset
+ @jour = @jours[@offset, @limit]
+ @state = false
+ end
+ # end
+
+ def user_comments
+
+ end
+
+ #end
+ def index
+ @status = params[:status] || 1
+ sort_init 'login', 'asc'
+ sort_update %w(login firstname lastname mail admin created_on last_login_on)
+
+ case params[:format]
+ when 'xml', 'json'
+ @offset, @limit = api_offset_and_limit({:limit => 15})
+ else
+ @limit = 15
+ end
+
+ # retrieve all users
+ # 先内连一下statuses 保证排序之后数量一致
+ scope = User.visible.
+ joins("INNER JOIN user_statuses ON users.id = user_statuses.user_id")
+
+ # unknow
+ scope = scope.in_group(params[:group_id]) if params[:group_id].present?
+
+ # pagination
+ @user_count = scope.count
+ @user_pages = Paginator.new @user_count, @limit, params['page']
+
+ # users classify
+ case params[:user_sort_type]
+ when '0'
+ # 创建时间排序
+ @s_type = 0
+ @users = scope.reorder('users.created_on DESC')
+ when '1'
+ # 活跃度排序, 就是所谓的得分情况
+ @s_type = 1
+ @users = scope.
+ joins("LEFT JOIN option_numbers ON users.id = option_numbers.user_id and option_numbers.score_type = 1").
+ reorder('option_numbers.total_score DESC')
+ when '2'
+ # 粉丝数排序
+ @s_type = 2
+ @users = scope.
+ #joins("INNER JOIN user_statuses ON users.id = user_statuses.user_id").
+ reorder('user_statuses.watchers_count DESC')
+
+ else
+ # 默认活跃度排序
+ @s_type = 1
+ @users = scope.
+ joins("LEFT JOIN option_numbers ON users.id = option_numbers.user_id and option_numbers.score_type = 1").
+ reorder('option_numbers.total_score DESC')
+ end
+
+ # limit and offset
+ @users = @users.limit(@user_pages.per_page).offset(@user_pages.offset)
+
+ @user_base_tag = params[:id] ? 'base_users':'users_base'
+ respond_to do |format|
+ format.html {
+ @groups = Group.all.sort
+ render :layout => @user_base_tag
+ }
+ format.api
+ end
+ end
+
+ def search
+ sort_init 'login', 'asc'
+ sort_update %w(login firstname lastname mail admin created_on last_login_on)
+ (redirect_to users_path, :notice => l(:label_sumbit_empty);return) if params[:name].blank?
+ case params[:format]
+ when 'xml', 'json'
+ @offset, @limit = api_offset_and_limit({:limit => 15})
+ else
+ @limit = 15#per_page_option
+ end
+
+ @status = params[:status] || 1
+ has = {
+ "show_changesets" => true
+ }
+ scope = User.logged.status(@status)
+ scope = scope.like(params[:name]) if params[:name].present?
+ @user_count = scope.count
+ @user_pages = Paginator.new @user_count, @limit, params['page']
+ @user_base_tag = params[:id] ? 'base_users':'users_base'
+ @offset ||= @user_pages.reverse_offset
+ unless @offset == 0
+ @users = scope.offset(@offset).limit(@limit).all.reverse
+ else
+ limit = @user_count % @limit
+ if limit == 0
+ limit = @limit
+ end
+ @users = scope.offset(@offset).limit(limit).all.reverse
+ end
+
+ respond_to do |format|
+ format.html {
+ @groups = Group.all.sort
+ render :layout => @user_base_tag
+ }
+ format.api
+ end
+ end
+
+ def show
+ pre_count = 10 #limit
+ case params[:type]
+ when "1"
+ if @user == User.current
+ activity = Activity.where('user_id = ?', User.current.id).order('id desc')
+ @activity_count = activity.count
+ @activity_pages = Paginator.new @activity_count, pre_count, params['page']
+ @activity = activity.offset(@activity_pages.offset).limit(@activity_pages.per_page).all
+ @state = 1
+ end
+ when "2"
+ message = []
+ if @user == User.current
+ message = JournalsForMessage.reference_message(@user.id)
+ message += Journal.reference_message(@user.id)
+ end
+ @activity_count = message.size
+ @info_pages = Paginator.new @activity_count, pre_count, params['page']
+ messages = message.sort {|x,y| y.created_on <=> x.created_on }
+ @message = messages[@info_pages.offset, @info_pages.per_page]
+ @state = 2
+ else
+ where_condition = nil;
+ # where_condition = "act_type <> 'JournalsForMessage'"
+ if @user == User.current
+ watcher = User.watched_by(@user)
+ watcher.push(User.current)
+ activity = Activity.where(where_condition).where('user_id in (?)', watcher).order('id desc')
+ else
+ activity = Activity.where(where_condition).where('user_id = ?', @user.id).order('id desc')
+ end
+ @activity_count = activity.count
+ @activity_pages = Paginator.new @activity_count, pre_count, params['page']
+ @activity = activity.offset(@activity_pages.offset).limit(@activity_pages.per_page).all
+ @state = 0
+ end
+
+ if params[:user].present?
+
+ user_temp = User.find_by_sql("select id from users where concat(lastname,firstname) like '%#{params[:user]}%' or lastname like '%#{params[:user]}%'")
+
+ if user_temp.size > 1
+ activity = Activity.where('user_id in (?)', user_temp).where('user_id in (?)', watcher).order('id desc')
+ elsif user_temp.size == 1
+ activity = Activity.where('user_id = ?', user_temp).where('user_id in (?)', watcher).order('id desc')
+ else
+ activity = Activity.where("1 = 0")
+ end
+ @offset, @limit = api_offset_and_limit({:limit => 10})
+ @activity_count = activity.count
+ @activity_pages = Paginator.new @activity_count, @limit, params['page']
+ @offset ||= @activity_pages.offset
+ @activity = activity.offset(@offset).limit(@limit)
+ @state = 0
+ end
+
+
+#Modified by nie
+ unless User.current.admin?
+ if !@user.active? #|| (@user != User.current && @memberships.empty? && events.empty?)
+ # redirect_to home_path
+ render_404
+ return
+ end
+ end
+
+ respond_to do |format|
+ format.html
+ format.api
+ end
+ end
+
+ ##end fq
+
+ #### added by fq
+ def info
+
+ message = []
+ if @user == User.current
+ message = JournalsForMessage.reference_message(@user.id)
+ message += Journal.reference_message(@user.id)
+ end
+ @offset, @limit = api_offset_and_limit({:limit => 10})
+ @info_count = message.size
+ @info_pages = Paginator.new @info_count, @limit, params['page']
+ @offset ||= @info_pages.offset
+
+ messages = message.sort {|x,y| y.created_on <=> x.created_on }
+
+ @message = messages[@offset, @limit]
+
+ unless User.current.admin?
+ if !@user.active?
+ render_404
+ return
+ end
+ end
+
+ respond_to do |format|
+ format.html
+ format.api
+ end
+ end
+ #### end
+
+
+ def new
+ @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
+ @auth_sources = AuthSource.all
+ render :layout => "users_base"
+ end
+
+ def create
+ @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
+ @user.safe_attributes = params[:user]
+ @user.admin = params[:user][:admin] || false
+ @user.login = params[:user][:login]
+ @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
+
+ if @user.save
+ @user.pref.attributes = params[:pref]
+ @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
+ @user.pref.save
+ @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
+
+ Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
+
+ respond_to do |format|
+ format.html {
+ flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
+ if params[:continue]
+ redirect_to new_user_path
+ else
+ redirect_to edit_user_path(@user)
+ end
+ }
+ format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
+ end
+ else
+ @auth_sources = AuthSource.all
+ # Clear password input
+ @user.password = @user.password_confirmation = nil
+
+ respond_to do |format|
+ format.html { render :action => 'new',:layout => "users_base" }
+ format.api { render_validation_errors(@user) }
+ end
+ end
+
+ unless @user.id.nil?
+ #后台注册的用户默认权限为男性开发员
+ ue = UserExtensions.create(:identity => 3,
+ :gender => 0,
+ :user_id => @user.id)
+ ue.save
+ end
+ end
+
+ def edit
+ @auth_sources = AuthSource.all
+ @membership ||= Member.new
+ end
+
+ def watch_projects
+ @watch_projects = Project.joins(:watchers).where("project_type <>? and watchable_type = ? and `watchers`.user_id = ?", '1','Project', @user.id)
+ @state = 1
+ respond_to do |format|
+ format.html {
+ render :layout => 'base_users'
+ }
+ format.api
+ end
+ end
+
+ def update
+ @user.admin = params[:user][:admin] if params[:user][:admin]
+ @user.login = params[:user][:login] if params[:user][:login]
+ if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
+ @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
+ end
+ @user.safe_attributes = params[:user]
+ # Was the account actived ? (do it before User#save clears the change)
+ was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
+ # TODO: Similar to My#account
+ @user.pref.attributes = params[:pref]
+ @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
+
+ if @user.save
+ @user.pref.save
+ @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
+
+ if was_activated
+ Mailer.account_activated(@user).deliver
+ elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
+ Mailer.account_information(@user, params[:user][:password]).deliver
+ end
+
+ respond_to do |format|
+ format.html {
+ flash[:notice] = l(:notice_successful_update)
+ redirect_to_referer_or edit_user_path(@user)
+ }
+ format.api { render_api_ok }
+ end
+ else
+ @auth_sources = AuthSource.all
+ @membership ||= Member.new
+ # Clear password input
+ @user.password = @user.password_confirmation = nil
+
+ respond_to do |format|
+ format.html { render :action => :edit }
+ format.api { render_validation_errors(@user) }
+ end
+ end
+ end
+
+ def destroy
+ @user.destroy
+ respond_to do |format|
+ format.html { redirect_back_or_default(admin_users_path) }
+ format.api { render_api_ok }
+ end
+ end
+
+ def edit_membership
+ @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
+ @membership.save
+ respond_to do |format|
+ format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
+ format.js
+ end
+ end
+
+ def destroy_membership
+ @membership = Member.find(params[:membership_id])
+ if @membership.deletable?
+ @membership.destroy
+ end
+ respond_to do |format|
+ format.html { redirect_to edit_user_path(@user, :tab => 'memberships') }
+ format.js
+ end
+ end
+
+ ################# added by william
+ def tag_save
+ @tags = params[:tag_for_save][:name]
+ @obj_id = params[:tag_for_save][:object_id]
+ @obj_flag = params[:tag_for_save][:object_flag]
+
+ case @obj_flag
+ when '1' then
+ @obj = User.find_by_id(@obj_id)
+ when '2' then
+ @obj = Project.find_by_id(@obj_id)
+ when '3' then
+ @obj = Issue.find_by_id(@obj_id)
+ when '4' then
+ @obj = Bid.find_by_id(@obj_id)
+ when '5' then
+ @obj = Forum.find_by_id(@obj_id)
+ when '6'
+ @obj = Attachment.find_by_id(@obj_id)
+ when '7' then
+ @obj = Contest.find_by_id(@obj_id)
+ when '8'
+ @obj = OpenSourceProject.find_by_id(@obj_id)
+ when '9'
+ @obj = Course.find_by_id(@obj_id)
+ else
+ @obj = nil
+ end
+ unless @obj.nil?
+ @obj.tag_list.add(@tags.split(","))
+ else
+ return
+ end
+ if @obj.save
+ logger.debug "#{__FILE__}:#{__LINE__} ===> #{@obj.to_json}"
+ else
+ logger.error "#{__FILE__}:#{__LINE__} ===> #{@obj.errors.try(:full_messages)}"
+ end
+ respond_to do |format|
+ format.js
+ format.html
+ end
+ end
+
+ def tag_saveEx
+ @tags = params[:tag_name]
+ @obj_id = params[:obj_id]
+ @obj_flag = params[:obj_flag]
+
+ case @obj_flag
+ when '1' then
+ @obj = User.find_by_id(@obj_id)
+ when '2' then
+ @obj = Project.find_by_id(@obj_id)
+ when '3' then
+ @obj = Issue.find_by_id(@obj_id)
+ when '4' then
+ @obj = Bid.find_by_id(@obj_id)
+ when '5' then
+ @obj = Forum.find_by_id(@obj_id)
+ when '6'
+ @obj = Attachment.find_by_id(@obj_id)
+ when '7' then
+ @obj = Contest.find_by_id(@obj_id)
+ when '8'
+ @obj = OpenSourceProject.find_by_id(@obj_id)
+ when '9'
+ @obj = Course.find_by_id(@obj_id)
+ else
+ @obj = nil
+ end
+ unless @obj.nil?
+ @obj.tag_list.add(@tags.split(","))
+ else
+ return
+ end
+ if @obj.save
+ ## 执行成功的操作。
+ else
+ #捕获异常
+ end
+ respond_to do |format|
+ format.js
+ format.html
+ end
+ end
+ ###add by huang
+ def user_watchlist
+ end
+ ###add by huang
+ def user_fanslist
+
+ end
+
+ #william
+ def update_extensions(user_extensions)
+ user_extensions = params[:user_extensions]
+ unless user_extensions.nil?
+ user_extensions = UserExtensions.find_by_id(user_extensions.user_id)
+
+ # user_extensions.
+ end
+ end
+
+# added by bai
+ def topic_score_index
+
+ end
+
+ def project_score_index
+
+ end
+
+ def activity_score_index
+
+ end
+
+ def influence_score_index
+
+ end
+
+ def score_index
+
+ end
+# end
+ def topic_new_score_index
+
+ end
+
+ def project_new_score_index
+
+ end
+
+ def activity_new_score_index
+
+ end
+
+ def influence_new_score_index
+
+ end
+
+ def score_new_index
+
+ end
+
+ def update_score
+ @user = User.find(params[:id])
+ end
+
+ private
+
+ def find_user
+ if params[:id] == 'current'
+ require_login || return
+ @user = User.current
+ else
+ @user = User.find(params[:id])
+ end
+ rescue ActiveRecord::RecordNotFound
+ render_404
+ end
+
+ def setting_layout(default_base='base_users')
+ User.current.admin? ? default_base : default_base
+ end
+
+ # 必填自己的工作单位,其实就是学校
+ def auth_user_extension
+ if @user == User.current && (@user.user_extensions.nil? || @user.user_extensions.school.nil?)
+ flash[:error] = l(:error_complete_occupation)
+ redirect_to my_account_path
+ end
+ end
+
+ #重置用户得分
+ def rest_user_score
+ memo_num(@user)
+ messges_for_issue_num(@user)
+ issues_status_num(@user)
+ replay_for_memo_num(@user)
+ tread_num(@user)
+ praise_num(@user)
+ changeset_num(@user)
+ document_num(@user)
+ attachment_num(@user)
+ issue_done_ratio_num(@user)
+ post_issue_num(@user)
+ end
+
+ #验证是否显示课程
+ def can_show_course
+ @first_page = FirstPage.where("page_type = 'project'").first
+ if @first_page.show_course == 2
+ render_404
+ end
+ end
+end
diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb
index 5824848ff..c3aff06e1 100644
--- a/app/controllers/versions_controller.rb
+++ b/app/controllers/versions_controller.rb
@@ -1,200 +1,200 @@
-# 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 VersionsController < ApplicationController
- layout "base_projects"
- menu_item :roadmap
- model_object Version
- before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
- #before_filter :find_model_object_contest, :except => [:index, :new, :create]
- before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
- before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
- before_filter :authorize
-
- accept_api_auth :index, :show, :create, :update, :destroy
-
- helper :custom_fields
- helper :projects
- helper :project_score
-
- def index
- respond_to do |format|
- format.html {
- @trackers = @project.trackers.sorted.all
- retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
- @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
- project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
-
- @versions = @project.shared_versions || []
- @versions += @project.rolled_up_versions.visible if @with_subprojects
- #added by young
- @versions = @versions.uniq.reverse#Modified by young
- unless params[:completed]
- @completed_versions = @versions.select {|version| version.closed? || version.completed? }
- @versions -= @completed_versions
- end
- @offset, @limit = api_offset_and_limit({:limit => 4})
- @versions_count = @versions.count
- @versions_pages = Paginator.new @versions_count, @limit, params['page']
- @offset ||= @versions_pages.offset
- @versions = @versions.slice(@offset, @limit)
- #end by young
-
-
- @issues_by_version = {}
- if @selected_tracker_ids.any? && @versions.any?
- issues = Issue.visible.all(
- :include => [:project, :status, :tracker, :priority, :fixed_version],
- :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
- :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
- )
- @issues_by_version = issues.group_by(&:fixed_version)
- end
- @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
- }
- format.api {
- @versions = @project.shared_versions.all
- }
- end
- end
-
- def show
- respond_to do |format|
- format.html {
- @issues = @version.fixed_issues.visible.
- includes(:status, :tracker, :priority).
- reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
- all
- }
- format.api
- end
- end
-
- def new
- @version = @project.versions.build
- @version.safe_attributes = params[:version]
-
- respond_to do |format|
- format.html
- format.js
- end
- end
-
- def create
- @version = @project.versions.build
- if params[:version]
- attributes = params[:version].dup
- attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
- @version.safe_attributes = attributes
- end
-
- if request.post?
- if @version.save
- respond_to do |format|
- format.html do
- flash[:notice] = l(:notice_successful_create)
- redirect_to settings_project_path(@project, :tab => 'versions')
- end
- format.js
- format.api do
- render :action => 'show', :status => :created, :location => version_url(@version)
- end
- end
- else
- respond_to do |format|
- format.html { render :action => 'new' }
- format.js { render :action => 'new' }
- format.api { render_validation_errors(@version) }
- end
- end
- end
- end
-
- def edit
- end
-
- def update
- if request.put? && params[:version]
- attributes = params[:version].dup
- attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
- @version.safe_attributes = attributes
- if @version.save
- respond_to do |format|
- format.html {
- flash[:notice] = l(:notice_successful_update)
- redirect_back_or_default settings_project_path(@project, :tab => 'versions')
- }
- format.api { render_api_ok }
- end
- else
- respond_to do |format|
- format.html { render :action => 'edit' }
- format.api { render_validation_errors(@version) }
- end
- end
- end
- end
-
- def close_completed
- if request.put?
- @project.close_completed_versions
- end
- redirect_to settings_project_path(@project, :tab => 'versions')
- end
-
- def close_completed_contest
- if request.put?
- @contest.close_completed_versions
- end
- redirect_to settings_contest_path(@contest, :tab => 'versions')
- end
-
- def destroy
- if @version.fixed_issues.empty?
- @version.destroy
- respond_to do |format|
- format.html { redirect_back_or_default settings_project_path(@project, :tab => 'versions') }
- format.api { render_api_ok }
- end
- else
- respond_to do |format|
- format.html {
- flash[:error] = l(:notice_unable_delete_version)
- redirect_to settings_project_path(@project, :tab => 'versions')
- }
- format.api { head :unprocessable_entity }
- end
- end
- end
-
- def status_by
- respond_to do |format|
- format.html { render :action => 'show' }
- format.js
- end
- end
-
- private
-
- def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
- if ids = params[:tracker_ids]
- @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
- else
- @selected_tracker_ids = (selectable_trackers).collect {|t| t.id.to_s }
- 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 VersionsController < ApplicationController
+ layout "base_projects"
+ menu_item :roadmap
+ model_object Version
+ before_filter :find_model_object, :except => [:index, :new, :create, :close_completed]
+ #before_filter :find_model_object_contest, :except => [:index, :new, :create]
+ before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed]
+ before_filter :find_project_by_project_id, :only => [:index, :new, :create, :close_completed]
+ before_filter :authorize
+
+ accept_api_auth :index, :show, :create, :update, :destroy
+
+ helper :custom_fields
+ helper :projects
+ helper :project_score
+
+ def index
+ respond_to do |format|
+ format.html {
+ @trackers = @project.trackers.sorted.all
+ retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
+ @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
+ project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
+
+ @versions = @project.shared_versions || []
+ @versions += @project.rolled_up_versions.visible if @with_subprojects
+ #added by young
+ @versions = @versions.uniq.reverse#Modified by young
+ unless params[:completed]
+ @completed_versions = @versions.select {|version| version.closed? || version.completed? }
+ @versions -= @completed_versions
+ end
+ @offset, @limit = api_offset_and_limit({:limit => 4})
+ @versions_count = @versions.count
+ @versions_pages = Paginator.new @versions_count, @limit, params['page']
+ @offset ||= @versions_pages.offset
+ @versions = @versions.slice(@offset, @limit)
+ #end by young
+
+
+ @issues_by_version = {}
+ if @selected_tracker_ids.any? && @versions.any?
+ issues = Issue.visible.all(
+ :include => [:project, :status, :tracker, :priority, :fixed_version],
+ :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids, :fixed_version_id => @versions.map(&:id)},
+ :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id"
+ )
+ @issues_by_version = issues.group_by(&:fixed_version)
+ end
+ @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
+ }
+ format.api {
+ @versions = @project.shared_versions.all
+ }
+ end
+ end
+
+ def show
+ respond_to do |format|
+ format.html {
+ @issues = @version.fixed_issues.visible.
+ includes(:status, :tracker, :priority).
+ reorder("#{Tracker.table_name}.position, #{Issue.table_name}.id").
+ all
+ }
+ format.api
+ end
+ end
+
+ def new
+ @version = @project.versions.build
+ @version.safe_attributes = params[:version]
+
+ respond_to do |format|
+ format.html
+ format.js
+ end
+ end
+
+ def create
+ @version = @project.versions.build
+ if params[:version]
+ attributes = params[:version].dup
+ attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
+ @version.safe_attributes = attributes
+ end
+
+ if request.post?
+ if @version.save
+ respond_to do |format|
+ format.html do
+ flash[:notice] = l(:notice_successful_create)
+ redirect_to settings_project_path(@project, :tab => 'versions')
+ end
+ format.js
+ format.api do
+ render :action => 'show', :status => :created, :location => version_url(@version)
+ end
+ end
+ else
+ respond_to do |format|
+ format.html { render :action => 'new' }
+ format.js { render :action => 'new' }
+ format.api { render_validation_errors(@version) }
+ end
+ end
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if request.put? && params[:version]
+ attributes = params[:version].dup
+ attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
+ @version.safe_attributes = attributes
+ if @version.save
+ respond_to do |format|
+ format.html {
+ flash[:notice] = l(:notice_successful_update)
+ redirect_back_or_default settings_project_path(@project, :tab => 'versions')
+ }
+ format.api { render_api_ok }
+ end
+ else
+ respond_to do |format|
+ format.html { render :action => 'edit' }
+ format.api { render_validation_errors(@version) }
+ end
+ end
+ end
+ end
+
+ def close_completed
+ if request.put?
+ @project.close_completed_versions
+ end
+ redirect_to settings_project_path(@project, :tab => 'versions')
+ end
+
+ def close_completed_contest
+ if request.put?
+ @contest.close_completed_versions
+ end
+ redirect_to settings_contest_path(@contest, :tab => 'versions')
+ end
+
+ def destroy
+ if @version.fixed_issues.empty?
+ @version.destroy
+ respond_to do |format|
+ format.html { redirect_back_or_default settings_project_path(@project, :tab => 'versions') }
+ format.api { render_api_ok }
+ end
+ else
+ respond_to do |format|
+ format.html {
+ flash[:error] = l(:notice_unable_delete_version)
+ redirect_to settings_project_path(@project, :tab => 'versions')
+ }
+ format.api { head :unprocessable_entity }
+ end
+ end
+ end
+
+ def status_by
+ respond_to do |format|
+ format.html { render :action => 'show' }
+ format.js
+ end
+ end
+
+ private
+
+ def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil)
+ if ids = params[:tracker_ids]
+ @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
+ else
+ @selected_tracker_ids = (selectable_trackers).collect {|t| t.id.to_s }
+ end
+ end
+end
diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb
index 8f0723f2e..ca168ccb8 100644
--- a/app/controllers/welcome_controller.rb
+++ b/app/controllers/welcome_controller.rb
@@ -1,200 +1,200 @@
-# 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 WelcomeController < ApplicationController
- include ApplicationHelper
- include WelcomeHelper
- helper :project_score
- caches_action :robots
- before_filter :find_first_page, :only => [:index]
- # before_filter :fake, :only => [:index, :course]
- before_filter :entry_select, :only => [:index]
-
- def index
- #@first_page = FirstPage.where("page_type = 'project'").first
- #@show_course = @first_page.show_course
- if @first_page.nil? || @first_page.sort_type.nil?
- @projects = find_miracle_project(10, 3,"score desc")
- else
- case @first_page.sort_type
- when 0
- @projects = find_miracle_project(10, 3,"created_on desc")
- #@projects = @projects_all.order("created_on desc")
- when 1
- @projects = find_miracle_project(10, 3,"score desc")
- #@projects = @projects_all.order("grade desc")
- when 2
- @projects = find_miracle_project(10, 3,"watchers_count desc")
- #@projects = @projects_all.order("watchers_count desc")
-
- #gcm
- #when '3'
- #@projects=desc_sort_course_by_avtivity(@project_activity_count_array,@project_all_array)
- # @projects=handle_project @projects_all,@project_activity_count
- # @s_type = 3
- # @projects = @projects[@project_pages.offset, @project_pages.per_page]
-
- else
- @projects = @projects_all.order("score desc")
- end
- end
-
- end
-
- def robots
- @projects = Project.all_public.active
- render :layout => false, :content_type => 'text/plain'
- end
-
- def course
- @course_page = FirstPage.where("page_type = 'course'").first
- if params[:school_id]
- @school_id = params[:school_id]
- elsif User.current.logged? && User.current.user_extensions.try(:school)
- @school_id = User.current.user_extensions.school.try(:id)
- end
- @logoLink ||= logolink()
- end
-
-
-
- def logolink()
- @course_page = FirstPage.where("page_type = 'course'").first
- logo = get_avatar?(@course_page)
- id = params[:school_id]
- logo_link = ""
- if id.nil? && (User.current.user_extensions.nil? || User.current.user_extensions.school.nil?)
- if logo
- logo_link = url_to_avatar(@course_page)
- else
- logo_link = '/images/transparent.png'
- end
-
- else
- if id == "0"
- if logo
- logo_link = url_to_avatar(@course_page)
- else
- logo_link = '/images/transparent.png'
- end
- else
- if id.nil?
- if School.find(User.current.user_extensions.school.id).logo_link.nil?
- logo_link = '/images/transparent.png'
- else
- logo_link = School.find(User.current.user_extensions.school.id).logo_link
- end
- else
- logo_link = School.find(id).logo_link
-
- end
- end
- end
- return logo_link
- end
-
-
- def contest
- @contest_page = FirstPage.where("page_type = 'contest'").first
- @contest_notifications = ContestNotification.order("id desc")
- end
-
- def search
- search_condition = params[:q]
- search_type = params[:search_type].to_sym unless search_condition.blank?
-
- if search_type.nil? && params[:contests_search] && params[:name] != ""
- search_type = :contests
- search_condition = params[:name]
- end
-
- respond_to do |format|
- format.html{
- case search_type
- when :projects
- redirect_to projects_search_path(:name => search_condition,
- :project_type => Project::ProjectType_project)
- when :courses
- redirect_to courses_search_path(:name => search_condition)
- when :contests
- redirect_to contests_path(:name => search_condition)
- when :users
- redirect_to users_search_path(:name => search_condition)
- when :users_teacher
- redirect_to users_search_path(:name => search_condition, :role => :teacher)
- when :users_student
- redirect_to users_search_path(:name => search_condition, :role => :student)
- else
- #redirect_to home_path, :alert => l(:label_sumbit_empty)
- (redirect_to home_path, :notice => l(:label_sumbit_empty);return) #if params[:name].blank?
- end
- }
- end
- end
-
- private
- # 判断网站的入口,是课程 course 则跳过index去渲染 course 方法
- def entry_select
- #@first_page = FirstPage.where("page_type = 'project'").first
- url = request.original_url.gsub('/','')
- if url.include?(Setting.host_course.gsub('/',''))
- if @first_page.show_course == 1
- course
- render :course
- else
- render_404
- end
-
- return 0
- elsif url.include?(Setting.host_contest.gsub('/',''))
- if @first_page.show_contest == 1
- contest
- render :contest
- else
- render_404
- end
-
- return 0
- elsif url.include?(Setting.host_user.gsub('/',''))
- redirect_to(:controller => "users", :action => "index")
- end
-
-
- end
-
- # def render(*args)
- # _fake if @fake_filter
- # super
- # end
-
- # private
-
- # def fake
- # @fake_filter = true
- # end
-
- # # 骗子方法
- # def _fake
- # instance_variables.map { |variable|
- # if variable.to_s =~ /Count$/
- # self.instance_variable_set(variable.to_sym,
- # ("1" + (self.instance_variable_get(variable.to_sym).to_s)).to_i)
- # 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 WelcomeController < ApplicationController
+ include ApplicationHelper
+ include WelcomeHelper
+ helper :project_score
+ caches_action :robots
+ before_filter :find_first_page, :only => [:index]
+ # before_filter :fake, :only => [:index, :course]
+ before_filter :entry_select, :only => [:index]
+
+ def index
+ #@first_page = FirstPage.where("page_type = 'project'").first
+ #@show_course = @first_page.show_course
+ if @first_page.nil? || @first_page.sort_type.nil?
+ @projects = find_miracle_project(10, 3,"score desc")
+ else
+ case @first_page.sort_type
+ when 0
+ @projects = find_miracle_project(10, 3,"created_on desc")
+ #@projects = @projects_all.order("created_on desc")
+ when 1
+ @projects = find_miracle_project(10, 3,"score desc")
+ #@projects = @projects_all.order("grade desc")
+ when 2
+ @projects = find_miracle_project(10, 3,"watchers_count desc")
+ #@projects = @projects_all.order("watchers_count desc")
+
+ #gcm
+ #when '3'
+ #@projects=desc_sort_course_by_avtivity(@project_activity_count_array,@project_all_array)
+ # @projects=handle_project @projects_all,@project_activity_count
+ # @s_type = 3
+ # @projects = @projects[@project_pages.offset, @project_pages.per_page]
+
+ else
+ @projects = @projects_all.order("score desc")
+ end
+ end
+
+ end
+
+ def robots
+ @projects = Project.all_public.active
+ render :layout => false, :content_type => 'text/plain'
+ end
+
+ def course
+ @course_page = FirstPage.where("page_type = 'course'").first
+ if params[:school_id]
+ @school_id = params[:school_id]
+ elsif User.current.logged? && User.current.user_extensions.try(:school)
+ @school_id = User.current.user_extensions.school.try(:id)
+ end
+ @logoLink ||= logolink()
+ end
+
+
+
+ def logolink()
+ @course_page = FirstPage.where("page_type = 'course'").first
+ logo = get_avatar?(@course_page)
+ id = params[:school_id]
+ logo_link = ""
+ if id.nil? && (User.current.user_extensions.nil? || User.current.user_extensions.school.nil?)
+ if logo
+ logo_link = url_to_avatar(@course_page)
+ else
+ logo_link = '/images/transparent.png'
+ end
+
+ else
+ if id == "0"
+ if logo
+ logo_link = url_to_avatar(@course_page)
+ else
+ logo_link = '/images/transparent.png'
+ end
+ else
+ if id.nil?
+ if School.find(User.current.user_extensions.school.id).logo_link.nil?
+ logo_link = '/images/transparent.png'
+ else
+ logo_link = School.find(User.current.user_extensions.school.id).logo_link
+ end
+ else
+ logo_link = School.find(id).logo_link
+
+ end
+ end
+ end
+ return logo_link
+ end
+
+
+ def contest
+ @contest_page = FirstPage.where("page_type = 'contest'").first
+ @contest_notifications = ContestNotification.order("id desc")
+ end
+
+ def search
+ search_condition = params[:q]
+ search_type = params[:search_type].to_sym unless search_condition.blank?
+
+ if search_type.nil? && params[:contests_search] && params[:name] != ""
+ search_type = :contests
+ search_condition = params[:name]
+ end
+
+ respond_to do |format|
+ format.html{
+ case search_type
+ when :projects
+ redirect_to projects_search_path(:name => search_condition,
+ :project_type => Project::ProjectType_project)
+ when :courses
+ redirect_to courses_search_path(:name => search_condition)
+ when :contests
+ redirect_to contests_path(:name => search_condition)
+ when :users
+ redirect_to users_search_path(:name => search_condition)
+ when :users_teacher
+ redirect_to users_search_path(:name => search_condition, :role => :teacher)
+ when :users_student
+ redirect_to users_search_path(:name => search_condition, :role => :student)
+ else
+ #redirect_to home_path, :alert => l(:label_sumbit_empty)
+ (redirect_to home_path, :notice => l(:label_sumbit_empty);return) #if params[:name].blank?
+ end
+ }
+ end
+ end
+
+ private
+ # 判断网站的入口,是课程 course 则跳过index去渲染 course 方法
+ def entry_select
+ #@first_page = FirstPage.where("page_type = 'project'").first
+ url = request.original_url.gsub('/','')
+ if url.include?(Setting.host_course.gsub('/',''))
+ if @first_page.show_course == 1
+ course
+ render :course
+ else
+ render_404
+ end
+
+ return 0
+ elsif url.include?(Setting.host_contest.gsub('/',''))
+ if @first_page.show_contest == 1
+ contest
+ render :contest
+ else
+ render_404
+ end
+
+ return 0
+ elsif url.include?(Setting.host_user.gsub('/',''))
+ redirect_to(:controller => "users", :action => "index")
+ end
+
+
+ end
+
+ # def render(*args)
+ # _fake if @fake_filter
+ # super
+ # end
+
+ # private
+
+ # def fake
+ # @fake_filter = true
+ # end
+
+ # # 骗子方法
+ # def _fake
+ # instance_variables.map { |variable|
+ # if variable.to_s =~ /Count$/
+ # self.instance_variable_set(variable.to_sym,
+ # ("1" + (self.instance_variable_get(variable.to_sym).to_s)).to_i)
+ # end
+ # }
+ # end
+
+end
diff --git a/app/controllers/zipdown_controller.rb b/app/controllers/zipdown_controller.rb
index b7eb8afe9..0393206b7 100644
--- a/app/controllers/zipdown_controller.rb
+++ b/app/controllers/zipdown_controller.rb
@@ -1,175 +1,175 @@
-require 'zip'
-class ZipdownController < ApplicationController
- #查找项目(课程)
- before_filter :find_project_by_bid_id, :only => [:assort]
- #检查权限
- #勿删 before_filter :authorize, :only => [:assort,:download_user_homework]
- SAVE_FOLDER = "#{Rails.root}/files"
- OUTPUT_FOLDER = "#{Rails.root}/tmp/archiveZip"
-
- #通过作业Id找到项目(课程)
- def find_project_by_bid_id
- obj_class = params[:obj_class]
- obj_id = params[:obj_id]
- obj = obj_class.constantize.find(obj_id)
- case obj.class.to_s.to_sym
- when :Bid
- @project = obj.courses[0]
- end
- end
- def assort
- obj_class = params[:obj_class]
- obj_id = params[:obj_id]
- obj = obj_class.constantize.find(obj_id)
- zipfile = nil
- case obj.class.to_s.to_sym
- when :Bid
- zipfile = zip_bid obj
- else
- logger.error "[ZipDown#assort] ===> #{obj.class.to_s.to_sym} unKown !!"
- end
- send_file zipfile, :filename => obj.name+".zip", :type => detect_content_type(zipfile) if zipfile
-
- #rescue NameError, ActiveRecord::RecordNotFound => e
- #logger.error "[ZipDown] ===> #{e}"
- #@error = e
- end
-
- #下载某一学生的作业的所有文件
- def download_user_homework
- homework = HomeworkAttach.find params[:homework]
- if User.current.admin? || User.current.member_of_course?(homework.bid.courses.first)
- if homework != nil
- if homework.attachments.count > 0
- zipfile = zip_homework_by_user homework
- send_file zipfile, :filename => homework.name+".zip", :type => detect_content_type(zipfile) if(zipfile)
- else
- render file: 'public/no_file_found.html' , :layout => 'course_base'
- end
- else
- render file: 'public/file_not_found.html' , :layout => 'course_base'
- end
- else
- render_403 :message => :notice_not_authorized ,:layout => "course_base"
- end
- rescue => e
- render file: 'public/file_not_found.html' , :layout => 'course_base'
- end
-
- private
-
- def zip_user_bid(bid,user_id)
- # Todo: User Access Controll
-
- homeattaches = bid.homeworks.where("user_id = ?",user_id)
- # 得到每一个人所有文件打包的zip文件
- # 并将每一个人的zip打包为一个并返回路径
- user_zip_paths = homeattaches.map do |homeattach|
- zip_homework_by_user homeattach
- end
- #zipping "#{Time.now.to_i}_#{bid.name}.zip", user_zip_paths, OUTPUT_FOLDER
- user_zip_paths
- end
-
- def zip_bid(bid)
- # Todo: User Access Controll
-
- homeattaches = bid.homeworks
- #记录所有作业是不是有附件,有一个附件就改为true
- has_file = false
- # 得到每一个人所有文件打包的zip文件
- # 并将每一个人的zip打包为一个并返回路径
- user_zip_paths = homeattaches.map do |homeattach|
- if homeattach.attachments.count > 0
- zip_homework_by_user homeattach
- has_file = true unless has_file
- end
- end
- unless has_file
- render file: 'public/no_file_fond.html' , :layout => 'course_base'
- end
- zipping "#{Time.now.to_i}_#{bid.name}.zip", user_zip_paths, OUTPUT_FOLDER
-
- #@paths = homeworks_attach_path
- #zipfile = ziping homeworks_attach_path
- #send_file zipfile, :filename => bid.name,
- # :type => detect_content_type(zipfile)
- #rescue Errno::ENOENT => e
- # logger.error "[Errno::ENOENT] ===> #{e}"
- # @error = e
- end
-
- def zip_homework_by_user(homeattach)
- #if homeattach.attachments.count > 0
- homeworks_attach_path = []
- # 需要将所有homework.attachments遍历加入zip
- # 并且返回zip路径
- user_attaches_paths = homeattach.attachments.each do |attach|
- #length = attach.storage_path.length
- homeworks_attach_path << attach.diskfile#.to_s.slice((length+1)..-1)
- end
- zipping("#{homeattach.user.name.to_s}_#{Time.now.to_i}.zip", homeworks_attach_path, OUTPUT_FOLDER, true)
- #user_attaches_paths
- #end
- end
-
-
- def zipping(zip_name_refer, files_paths, output_path, is_attachment=false)
- # 输入待打包的文件列表,已经打包文件定位到ouput_path
- ic = Iconv.new('GBK//IGNORE', 'UTF-8//IGNORE')
- input_filename = files_paths
-
- rename_zipfile = zip_name_refer ||= "archive_#{Time.now.to_i}.zip"
- zipfile_name = "#{output_path}/#{rename_zipfile}"
-
- Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
-
- Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
- input_filename.each do |filename|
- rename_file = Time.now.to_i.to_s+ ic.iconv( (File.basename(filename)) ).to_s
- rename_file = ic.iconv( filename_to_real( File.basename(filename))).to_s if is_attachment
-
- zipfile.add(rename_file, filename)
- end
- zipfile.get_output_stream('ReadMe') do |os|
- os.write 'Homeworks'
- end
- end
- zipfile_name
- rescue Errno => e
- logger.error "[zipdown#zipping] ===> #{e}"
- @error = e
- end
-
- #def ziping files_path
- # ic = Iconv.new('GBK//IGNORE', 'UTF-8//IGNORE')
- # folder = SaveFolder
- # input_filename = files_path
- # zipfile_name = "#{OutputFolder}/archive_#{Time.now.to_i}.zip"
- #
- # Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
- # Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
- # input_filename.each do |filename|
- # zipfile.add(ic.iconv(filename_to_real(File.basename(filename))), folder + '/' + filename)
- # end
- # zipfile.get_output_stream("ReadMe") { |os|
- # os.write "Homeworks"
- # }
- # end
- # zipfile_name
- #rescue Errno => e
- # logger.error "[zipdown#zipping] ===> #{e}"
- # @error = e
- #end
-
- def detect_content_type(name)
- content_type = Redmine::MimeType.of(name)
- content_type.to_s
- end
-
- def filename_to_real(name)
- attach = Attachment.find_by_disk_filename(name)
- attach.filename
- end
-
-end
+require 'zip'
+class ZipdownController < ApplicationController
+ #查找项目(课程)
+ before_filter :find_project_by_bid_id, :only => [:assort]
+ #检查权限
+ #勿删 before_filter :authorize, :only => [:assort,:download_user_homework]
+ SAVE_FOLDER = "#{Rails.root}/files"
+ OUTPUT_FOLDER = "#{Rails.root}/tmp/archiveZip"
+
+ #通过作业Id找到项目(课程)
+ def find_project_by_bid_id
+ obj_class = params[:obj_class]
+ obj_id = params[:obj_id]
+ obj = obj_class.constantize.find(obj_id)
+ case obj.class.to_s.to_sym
+ when :Bid
+ @project = obj.courses[0]
+ end
+ end
+ def assort
+ obj_class = params[:obj_class]
+ obj_id = params[:obj_id]
+ obj = obj_class.constantize.find(obj_id)
+ zipfile = nil
+ case obj.class.to_s.to_sym
+ when :Bid
+ zipfile = zip_bid obj
+ else
+ logger.error "[ZipDown#assort] ===> #{obj.class.to_s.to_sym} unKown !!"
+ end
+ send_file zipfile, :filename => obj.name+".zip", :type => detect_content_type(zipfile) if zipfile
+
+ #rescue NameError, ActiveRecord::RecordNotFound => e
+ #logger.error "[ZipDown] ===> #{e}"
+ #@error = e
+ end
+
+ #下载某一学生的作业的所有文件
+ def download_user_homework
+ homework = HomeworkAttach.find params[:homework]
+ if User.current.admin? || User.current.member_of_course?(homework.bid.courses.first)
+ if homework != nil
+ if homework.attachments.count > 0
+ zipfile = zip_homework_by_user homework
+ send_file zipfile, :filename => homework.name+".zip", :type => detect_content_type(zipfile) if(zipfile)
+ else
+ render file: 'public/no_file_found.html' , :layout => 'course_base'
+ end
+ else
+ render file: 'public/file_not_found.html' , :layout => 'course_base'
+ end
+ else
+ render_403 :message => :notice_not_authorized ,:layout => "course_base"
+ end
+ rescue => e
+ render file: 'public/file_not_found.html' , :layout => 'course_base'
+ end
+
+ private
+
+ def zip_user_bid(bid,user_id)
+ # Todo: User Access Controll
+
+ homeattaches = bid.homeworks.where("user_id = ?",user_id)
+ # 得到每一个人所有文件打包的zip文件
+ # 并将每一个人的zip打包为一个并返回路径
+ user_zip_paths = homeattaches.map do |homeattach|
+ zip_homework_by_user homeattach
+ end
+ #zipping "#{Time.now.to_i}_#{bid.name}.zip", user_zip_paths, OUTPUT_FOLDER
+ user_zip_paths
+ end
+
+ def zip_bid(bid)
+ # Todo: User Access Controll
+
+ homeattaches = bid.homeworks
+ #记录所有作业是不是有附件,有一个附件就改为true
+ has_file = false
+ # 得到每一个人所有文件打包的zip文件
+ # 并将每一个人的zip打包为一个并返回路径
+ user_zip_paths = homeattaches.map do |homeattach|
+ if homeattach.attachments.count > 0
+ zip_homework_by_user homeattach
+ has_file = true unless has_file
+ end
+ end
+ unless has_file
+ render file: 'public/no_file_fond.html' , :layout => 'course_base'
+ end
+ zipping "#{Time.now.to_i}_#{bid.name}.zip", user_zip_paths, OUTPUT_FOLDER
+
+ #@paths = homeworks_attach_path
+ #zipfile = ziping homeworks_attach_path
+ #send_file zipfile, :filename => bid.name,
+ # :type => detect_content_type(zipfile)
+ #rescue Errno::ENOENT => e
+ # logger.error "[Errno::ENOENT] ===> #{e}"
+ # @error = e
+ end
+
+ def zip_homework_by_user(homeattach)
+ #if homeattach.attachments.count > 0
+ homeworks_attach_path = []
+ # 需要将所有homework.attachments遍历加入zip
+ # 并且返回zip路径
+ user_attaches_paths = homeattach.attachments.each do |attach|
+ #length = attach.storage_path.length
+ homeworks_attach_path << attach.diskfile#.to_s.slice((length+1)..-1)
+ end
+ zipping("#{homeattach.user.name.to_s}_#{Time.now.to_i}.zip", homeworks_attach_path, OUTPUT_FOLDER, true)
+ #user_attaches_paths
+ #end
+ end
+
+
+ def zipping(zip_name_refer, files_paths, output_path, is_attachment=false)
+ # 输入待打包的文件列表,已经打包文件定位到ouput_path
+ ic = Iconv.new('GBK//IGNORE', 'UTF-8//IGNORE')
+ input_filename = files_paths
+
+ rename_zipfile = zip_name_refer ||= "archive_#{Time.now.to_i}.zip"
+ zipfile_name = "#{output_path}/#{rename_zipfile}"
+
+ Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
+
+ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
+ input_filename.each do |filename|
+ rename_file = Time.now.to_i.to_s+ ic.iconv( (File.basename(filename)) ).to_s
+ rename_file = ic.iconv( filename_to_real( File.basename(filename))).to_s if is_attachment
+
+ zipfile.add(rename_file, filename)
+ end
+ zipfile.get_output_stream('ReadMe') do |os|
+ os.write 'Homeworks'
+ end
+ end
+ zipfile_name
+ rescue Errno => e
+ logger.error "[zipdown#zipping] ===> #{e}"
+ @error = e
+ end
+
+ #def ziping files_path
+ # ic = Iconv.new('GBK//IGNORE', 'UTF-8//IGNORE')
+ # folder = SaveFolder
+ # input_filename = files_path
+ # zipfile_name = "#{OutputFolder}/archive_#{Time.now.to_i}.zip"
+ #
+ # Dir.mkdir(File.dirname(zipfile_name)) unless File.exist?(File.dirname(zipfile_name))
+ # Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
+ # input_filename.each do |filename|
+ # zipfile.add(ic.iconv(filename_to_real(File.basename(filename))), folder + '/' + filename)
+ # end
+ # zipfile.get_output_stream("ReadMe") { |os|
+ # os.write "Homeworks"
+ # }
+ # end
+ # zipfile_name
+ #rescue Errno => e
+ # logger.error "[zipdown#zipping] ===> #{e}"
+ # @error = e
+ #end
+
+ def detect_content_type(name)
+ content_type = Redmine::MimeType.of(name)
+ content_type.to_s
+ end
+
+ def filename_to_real(name)
+ attach = Attachment.find_by_disk_filename(name)
+ attach.filename
+ end
+
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 807d851be..ef18793ab 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -33,8 +33,8 @@ module ApplicationHelper
extend Forwardable
def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
- #Added by young
- #Define the course menu's link class
+ # Added by young
+ # Define the course menu's link class
# 不是数组的转化成数组,然后判断当前menu_item是否在给定的列表
# REVIEW: 目测menu的机制,貌似不是很需要转换,再说
def link_class(label)
@@ -679,15 +679,15 @@ module ApplicationHelper
def textilizable(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
case args.size
- when 1
- obj = options[:object]
- text = args.shift
- when 2
- obj = args.shift
- attr = args.shift
- text = obj.send(attr).to_s
- else
- raise ArgumentError, 'invalid arguments to textilizable'
+ when 1
+ obj = options[:object]
+ text = args.shift
+ when 2
+ obj = args.shift
+ attr = args.shift
+ text = obj.send(attr).to_s
+ else
+ raise ArgumentError, 'invalid arguments to textilizable'
end
return '' if text.blank?
project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
diff --git a/app/helpers/attachments_helper.rb b/app/helpers/attachments_helper.rb
index ee9d2a70d..e6f20ea60 100644
--- a/app/helpers/attachments_helper.rb
+++ b/app/helpers/attachments_helper.rb
@@ -22,6 +22,9 @@ module AttachmentsHelper
# Options:
# :author -- author names are not displayed if set to false
# :thumbails -- display thumbnails if enabled in settings
+
+ include Redmine::Pagination
+
def link_to_attachments(container, options = {})
options.assert_valid_keys(:author, :thumbnails)
@@ -171,7 +174,9 @@ module AttachmentsHelper
s.html_safe
end
- def private_filter resultSet
+ # Modified by Longjun
+ # 有参数的方法要加()
+ def private_filter(resultSet)
result = resultSet.to_a.dup
# modify by nwb
@@ -190,8 +195,12 @@ module AttachmentsHelper
result
end
- include Redmine::Pagination
- def paginateHelper obj, pre_size=10
+ # Modified by Longjun
+ # include 应放在class/model 的开始处
+ # include Redmine::Pagination
+ # end
+
+ def paginateHelper (obj, pre_size=10)
@obj_count = obj.count
@obj_pages = Paginator.new @obj_count, pre_size, params['page']
if obj.kind_of? ActiveRecord::Base or obj.kind_of? ActiveRecord::Relation
diff --git a/app/helpers/contests_helper.rb b/app/helpers/contests_helper.rb
index 01c7d544f..0bc3e5e73 100644
--- a/app/helpers/contests_helper.rb
+++ b/app/helpers/contests_helper.rb
@@ -43,16 +43,16 @@ module ContestsHelper
def sort_contest_enterprise(state, project_type)
content = ''.html_safe
case state
- when 0
- content << content_tag('li', link_to(l(:label_sort_by_active), calls_path(:contest_sort_type => '1', :project_type => project_type)))
- content << content_tag('li', link_to(l(:label_sort_by_time), calls_path(:contest_sort_type => '0', :project_type => project_type), :class=>"selected"), :class=>"selected")
-
- when 1
- content << content_tag('li', link_to(l(:label_sort_by_active), calls_path(:contest_sort_type => '1', :project_type => project_type), :class=>"selected"), :class=>"selected")
- content << content_tag('li', link_to(l(:label_sort_by_time), calls_path(:contest_sort_type => '0', :project_type => project_type)))
- end
- content = content_tag('ul', content)
- content_tag('div', content, :class => "tabs_enterprise")
+ when 0
+ content << content_tag('li', link_to(l(:label_sort_by_active), calls_path(:contest_sort_type => '1', :project_type => project_type)))
+ content << content_tag('li', link_to(l(:label_sort_by_time), calls_path(:contest_sort_type => '0', :project_type => project_type), :class=>"selected"), :class=>"selected")
+
+ when 1
+ content << content_tag('li', link_to(l(:label_sort_by_active), calls_path(:contest_sort_type => '1', :project_type => project_type), :class=>"selected"), :class=>"selected")
+ content << content_tag('li', link_to(l(:label_sort_by_time), calls_path(:contest_sort_type => '0', :project_type => project_type)))
+ end
+ content = content_tag('ul', content)
+ content_tag('div', content, :class => "tabs_enterprise")
end
#end
@@ -100,7 +100,10 @@ module ContestsHelper
def count_contest_project
contests = Contest.find(:id)
@projects = []
- for contest in contests
+ # Modified by longjun
+ # for contest in contests
+ contests.each do |contest|
+ # end longjun
@projects += contest.contesting_projects
end
@projects.count
@@ -109,7 +112,10 @@ module ContestsHelper
def count_contest_softapplication
contests = Contest.find(:id)
@softapplications = []
- for contest in contests
+ # Modified by alan
+ # for contest in contests
+ contests.each do |contest|
+ # end alan
@softapplications += contest.contesting_softapplications
end
@projects.count
@@ -119,19 +125,29 @@ module ContestsHelper
def count_contest_user
contests = Contest.find(:id)
@users = []
- for contest in contests
- for project in contest.projects
+ # Modified by alan
+ # for contest in contests
+ contests.each do |contest|
+
+ contest.projects.each do |project|
+
@users += project.users
end
end
+ # end alan
+
@users.count
end
def count_contest_softapplication_user
contests = Contest.find(:id)
@users = []
- for contest in contests
- for project in contest.softapplications
+ # Modified by alan
+ # for contest in contests
+ contests.each do |contest|
+
+ contest.projects.each do |softapplications|
+ # for project in contest.softapplications
@users += softapplication.users
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 9fcebe640..dfacff864 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -44,7 +44,7 @@ module ProjectsHelper
content = ''.html_safe
case state
when 0
-
+
content << content_tag('li', link_to(l(:label_sort_by_active), projects_path(:project_sort_type => '1', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_influence), projects_path(:project_sort_type => '2', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_time), projects_path(:project_sort_type => '0', :project_type => project_type), :class=>"selected"), :class=>"selected")
@@ -70,19 +70,19 @@ module ProjectsHelper
content << content_tag('li', link_to(l(:label_sort_by_active), course_path(:project_sort_type => '1', :project_type => project_type, :school_id => school_id)))
# content << content_tag('li', link_to(l(:label_sort_by_influence), course_path(:project_sort_type => '2', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_activity), course_path(:project_sort_type => '3', :project_type => project_type, :school_id => school_id)))
-
+
when 1
content << content_tag('li', link_to(l(:label_sort_by_time), course_path(:project_sort_type => '0', :project_type => project_type, :school_id => school_id)))
content << content_tag('li', link_to(l(:label_sort_by_active), course_path(:project_sort_type => '1', :project_type => project_type, :school_id => school_id), :class=>"selected"), :class=>"selected")
# content << content_tag('li', link_to(l(:label_sort_by_influence), course_path(:project_sort_type => '2', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_activity), course_path(:project_sort_type => '3', :project_type => project_type, :school_id => school_id)))
-
+
when 2
content << content_tag('li', link_to(l(:label_sort_by_time), course_path(:project_sort_type => '0', :project_type => project_type, :school_id => school_id)))
content << content_tag('li', link_to(l(:label_sort_by_active), course_path(:project_sort_type => '1', :project_type => project_type, :school_id => school_id)))
# content << content_tag('li', link_to(l(:label_sort_by_influence), course_path(:project_sort_type => '2', :project_type => project_type), :class=>"selected"), :class=>"selected")
content << content_tag('li', link_to(l(:label_sort_by_activity), course_path(:project_sort_type => '3', :project_type => project_type, :school_id => school_id)))
-
+
#gcm
when 3
content << content_tag('li', link_to(l(:label_sort_by_time), course_path(:project_sort_type => '0', :project_type => project_type, :school_id => school_id)))
@@ -101,7 +101,7 @@ module ProjectsHelper
content = ''.html_safe
case state
when 0
-
+
content << content_tag('li', link_to(l(:label_sort_by_active), projects_path(:project_sort_type => '1', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_influence), projects_path(:project_sort_type => '2', :project_type => project_type)))
content << content_tag('li', link_to(l(:label_sort_by_time), projects_path(:project_sort_type => '0', :project_type => project_type), :class=>"selected"), :class=>"selected")
@@ -142,7 +142,7 @@ module ProjectsHelper
# end
- #Added by young
+ # Added by young
def course_settings_tabs
tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural, :course=>'1'},
#{:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural, :project_type => 1},
@@ -151,7 +151,7 @@ module ProjectsHelper
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
- #Ended by young
+ # Ended by young
@@ -173,14 +173,42 @@ module ProjectsHelper
# Renders the projects index
def render_project_hierarchy(projects)
render_project_nested_lists(projects) do |project|
- if (project.try(:project_type) == Project::ProjectType_course )
- s = project.is_public == 1 ? "".html_safe : "#{l(:label_private)} ".html_safe
- s += link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}").html_safe
- else
- s = project.is_public ? "".html_safe : "#{l(:label_private)} ".html_safe
- s += link_to_project(project, {}, :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
- end
+ #Modified by young
+ if project.try(:project_type) == Project::ProjectType_course
+
+ # modified by longjun
+ # never use unless and else
+ # unless project.is_public == 1
+
+ if project.is_public != 1
+ s = "#{l(:lable_private)} ".html_safe
+ else
+ s = "".html_safe
+ end
+ # end longjun
+
+ # modified by Longjun
+ s += link_to_project(project, {},
+ :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}").html_safe
+ # end longjun
+ else
+ # modified by longjun
+ # unless project.is_public
+
+ if !project.is_public
+ # end longjun
+ s = "#{l(:lable_private)} ".html_safe
+ else
+ s = "".html_safe
+ end
+ # modified by longjun
+ s += link_to_project(project, {},
+ :class => "#{project.css_classes} #{User.current.member_of?(project) ? 'my-project' : nil}")
+ # end longjun
+ end
+ #Ended by young
if project.description.present?
+ #Delete by nie.
# s << content_tag('td', textilizable(project.short_description, :project => project), :class => 'wiki description')
end
s
@@ -243,9 +271,14 @@ module ProjectsHelper
def is_manager?(user_id,project_id)
@result = false
@user_id = ProjectInfo.find_by_project_id(project_id)
- if @user_id == user.id
- @result = true
- end
+
+ # modified by longjun
+ # if @user_id == user.id
+ # @result = true
+ # end
+
+ @result = true if @user_id = user.id
+ # end longjun
return @result
end
@@ -330,9 +363,9 @@ module ProjectsHelper
return true if (project.nil? && project.course_extra.nil?)
courses_year = project.course_extra.time
current_year = Time.now.year
- if(courses_year >= current_year)
+ if courses_year >= current_year
return false
- elsif( (courses_year < current_year) && (Time.now.month < 3) )
+ elsif (courses_year < current_year) && (Time.now.month < 3)
return false
else
return true
diff --git a/app/models/contest.rb b/app/models/contest.rb
index f1503f593..b2c11ceac 100644
--- a/app/models/contest.rb
+++ b/app/models/contest.rb
@@ -72,11 +72,14 @@ class Contest < ActiveRecord::Base
end
end
- def self.creat_contests(budget, deadline, name, description=nil)
- self.create(:author_id => User.current.id, :budget => budget,
- :deadline => deadline, :name => name, :description => description, :commit => 0)
- end
-
+ # modified by longjun
+ # 这个函数没有用到
+ # def self.creat_contests(budget, deadline, name, description=nil)
+ # self.create(:author_id => User.current.id, :budget => budget,
+ # :deadline => deadline, :name => name, :description => description, :commit => 0)
+ # end
+ # end longjun
+
def update_contests(budget, deadline, name, description=nil)
if(User.current.id == self.author_id)
self.name = name
diff --git a/app/models/contesting_project.rb b/app/models/contesting_project.rb
index cb2a7fe39..641990b8a 100644
--- a/app/models/contesting_project.rb
+++ b/app/models/contesting_project.rb
@@ -14,7 +14,7 @@ class ContestingProject < ActiveRecord::Base
validate :validate_project
validates_uniqueness_of :contest_id, :scope => :project_id
- def self.cerate_contesting(contest_id, project_id, description = nil)
+ def self.create_contesting(contest_id, project_id, description = nil)
self.create(:user_id => User.current.id, :contest_id => contest_id,
:project_id => project_id, :description => description)
end
diff --git a/app/models/enabled_module.rb b/app/models/enabled_module.rb
index 94539ec00..fe668e02c 100644
--- a/app/models/enabled_module.rb
+++ b/app/models/enabled_module.rb
@@ -22,7 +22,6 @@ class EnabledModule < ActiveRecord::Base
validates_uniqueness_of :name, :scope => :project_id
after_create :module_enabled
-
private
# after_create callback used to do things when a module is enabled
@@ -35,4 +34,6 @@ class EnabledModule < ActiveRecord::Base
end
end
end
+
+
end
diff --git a/app/models/project.rb b/app/models/project.rb
index dfb6ccd2d..ddb92a587 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,1153 +1,1153 @@
-# 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 Project < ActiveRecord::Base
- include Redmine::SafeAttributes
- ProjectType_project = 0
- ProjectType_course = 1
-
- # Project statuses
- STATUS_ACTIVE = 1
- STATUS_CLOSED = 5
- STATUS_ARCHIVED = 9
-
- # Maximum length for project identifiers
- IDENTIFIER_MAX_LENGTH = 100
-
- # Specific overidden Activities
-
- belongs_to :homework_attach
- has_many :time_entry_activities
- has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
- has_many :memberships, :class_name => 'Member'
- has_many :member_principals, :class_name => 'Member',
- :include => :principal,
- :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE})"
- has_many :users, :through => :members
- has_many :principals, :through => :member_principals, :source => :principal
- has_many :enabled_modules, :dependent => :delete_all
- has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
- has_many :issues, :dependent => :destroy, :include => [:status, :tracker]
- has_many :issue_changes, :through => :issues, :source => :journals
- has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
- has_many :time_entries, :dependent => :delete_all
- has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
- has_many :documents, :dependent => :destroy
- has_many :news, :dependent => :destroy, :include => :author
- has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
- has_many :boards, :dependent => :destroy, :order => "position ASC"
- has_one :repository, :conditions => ["is_default = ?", true]
- has_many :repositories, :dependent => :destroy
- has_many :changesets, :through => :repository
- #added by xianbo for delete biding_project
- has_many :biding_projects, :dependent => :destroy
- has_many :contesting_projects, :dependent => :destroy
- has_many :softapplications, :through => :projecting_softapplications
- #ended by xianbo
- # added by fq
- has_many :journals_for_messages, :as => :jour, :dependent => :destroy
- #has_many :homework_for_courses, :dependent => :destroy
- #has_many :homeworks, :through => :homework_for_courses, :source => :bid, :dependent => :destroy
- has_many :shares, :dependent => :destroy
- # has_many :students_for_courses, :dependent => :destroy
- has_many :student, :through => :students_for_courses, :source => :user
- has_one :course_extra, :class_name => 'Course', :foreign_key => :extra,:primary_key => :identifier, :dependent => :destroy
- has_many :applied_projects
-
-
- # end
- #ADDED BY NIE
- has_many :project_infos, :dependent => :destroy
- has_one :project_status, :class_name => "ProjectStatus", :dependent => :destroy
- has_many :user_grades, :class_name => "UserGrade", :dependent => :destroy
- #end
-
- has_one :wiki, :dependent => :destroy
- ##added by xianbo
- has_one :course, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
- accepts_nested_attributes_for :course
- ##end
- # Custom field for the project issues
- has_and_belongs_to_many :issue_custom_fields,
- :class_name => 'IssueCustomField',
- :order => "#{CustomField.table_name}.position",
- :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
- :association_foreign_key => 'custom_field_id'
-
- has_many :tags, :through => :project_tags, :class_name => 'Tag'
- has_many :project_tags, :class_name => 'ProjectTags'
-
- # has_many :journals
-
- acts_as_nested_set :order => 'name', :dependent => :destroy
- acts_as_attachable :view_permission => :view_files,
- :delete_permission => :manage_files
-
- acts_as_customizable
- acts_as_searchable :columns => ['name', 'identifier', 'description'], :project_key => 'id', :permission => nil
- acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
- :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
- :author => nil
- ############################added by william
- acts_as_taggable
- scope :by_join_date, order("created_on DESC")
- ###################added by liuping 关注
- acts_as_watchable
-
- attr_protected :status
-
- validates_presence_of :name, :identifier
- validates_uniqueness_of :identifier
- validates_uniqueness_of :name
- validates_associated :repository, :wiki
- # validates_length_of :description, :maximum => 255
- validates_length_of :name, :maximum => 255
- validates_length_of :homepage, :maximum => 255
- validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH
- # donwcase letters, digits, dashes but not digits only
- validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :if => Proc.new { |p| p.identifier_changed? }
- # reserved words
- validates_exclusion_of :identifier, :in => %w( new )
-
- #此代码功能:为原redmine中项目的树形结构按名称首字母排序,本系统项目非树形结构,且项目排序方式无按首字母排序,另该代码执行会使空数据库时创建项目时出异常故注释掉
- #after_save :update_position_under_parent, :if => Proc.new {|project| project.name_changed?}
- after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?}
- # 创建project之后默认创建一个board,之后的board去掉了board的概念
- after_create :create_board_sync
- before_destroy :delete_all_members
- def remove_references_before_destroy
- return if self.id.nil?
- Watcher.delete_all ['watchable_id = ?', id]
- end
- scope :has_module, lambda {|mod|
- where("#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s)
- }
- scope :active, lambda { where(:status => STATUS_ACTIVE) }
- scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
- scope :all_public, lambda { where(:is_public => true) }
- scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) }
- scope :allowed_to, lambda {|*args|
- user = User.current
- permission = nil
- if args.first.is_a?(Symbol)
- permission = args.shift
- else
- user = args.shift
- permission = args.shift
- end
- where(Project.allowed_to_condition(user, permission, *args))
- }
- scope :like, lambda {|arg|
- if arg.blank?
- where(nil)
- else
- pattern = "%#{arg.to_s.strip.downcase}%"
- where("LOWER(name) LIKE :p ", :p => pattern)
- end
- }
- scope :project_entities, -> { where(project_type: ProjectType_project) }
- scope :course_entities, -> { where(project_type: ProjectType_course) }
-
- def new_course
- self.where('project_type = ?', 1)
- end
-
- # 获取项目的资源类型列表
- def attachmenttypes
- @attachmenttypes = Attachmentstype.find(:all, :conditions => ["#{Attachmentstype.table_name}.typeId= ?",self.attachmenttype ])
- end
-
- # 获取资源后缀名列表
- def contenttypes
- attachmenttypes
- if @attachmenttypes.length >0
- @attachmenttypes.last().suffixArr
- end
- end
-
- #自定义验证
- def validation
- if !class_period.match([0-9])
- errors.add_to_base("class period can only digital")
- end
- end
-
-# 项目留言 added by fq
- def self.add_jour(user, notes)
- project = Project.find('trustie')
- # project.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => 0)
- pjfm = project.journals_for_messages.build(:user_id => user.id, :notes => notes, :reply_id => 0)
- pjfm.save
- pjfm
- end
-
- def self.add_new_jour(user, notes, id, options={})
- project = Project.find(id)
- if options.count == 0
- pjfm = project.journals_for_messages.build(:user_id => user.id, :notes => notes, :reply_id => 0)
- else
- pjfm = project.journals_for_messages.build(options)
- end
- pjfm.save
- pjfm
- end
-# end
-
- # 管理员的邮件列表
- def manager_recipients
- notified = project.project_infos.collect(&:user)
- notified.collect(&:mail)
- end
-
- def initialize(attributes=nil, *args)
- super
-
- initialized = (attributes || {}).stringify_keys
- if !initialized.key?('identifier') && Setting.sequential_project_identifiers?
- self.identifier = Project.next_identifier
- end
- if !initialized.key?('is_public')
- self.is_public = Setting.default_projects_public?
- end
- if !initialized.key?('enabled_module_names')
- self.enabled_module_names = Setting.default_projects_modules
- end
- if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
- default = Setting.default_projects_tracker_ids
- if default.is_a?(Array)
- self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all
- else
- self.trackers = Tracker.sorted.all
- end
- end
- end
-
- def identifier=(identifier)
- super unless identifier_frozen?
- end
-
- def identifier_frozen?
- errors[:identifier].blank? && !(new_record? || identifier.blank?)
- end
-
- # returns latest created projects
- # non public projects will be returned only if user is a member of those
- def self.latest(user=nil, count=5)
- visible(user).limit(count).order("created_on DESC").all
- end
-
- # Returns true if the project is visible to +user+ or to the current user.
- def visible?(user=User.current)
- user.allowed_to?(:view_project, self)
- end
-
- # Returns a SQL conditions string used to find all projects visible by the specified user.
- #
- # Examples:
- # Project.visible_condition(admin) => "projects.status = 1"
- # Project.visible_condition(normal_user) => "((projects.status = 1) AND (projects.is_public = 1 OR projects.id IN (1,3,4)))"
- # Project.visible_condition(anonymous) => "((projects.status = 1) AND (projects.is_public = 1))"
- def self.visible_condition(user, options={})
- allowed_to_condition(user, :view_project, options)
- end
-
- # Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+
- #
- # Valid options:
- # * :project => limit the condition to project
- # * :with_subprojects => limit the condition to project and its subprojects
- # * :member => limit the condition to the user projects
- def self.allowed_to_condition(user, permission, options={})
- perm = Redmine::AccessControl.permission(permission)
- base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}")
- if perm && perm.project_module
- # If the permission belongs to a project module, make sure the module is enabled
- base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')"
- end
- if options[:project]
- project_statement = "#{Project.table_name}.id = #{options[:project].id}"
- project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects]
- base_statement = "(#{project_statement}) AND (#{base_statement})"
- end
-
- if user.admin?
- base_statement
- else
- statement_by_role = {}
- unless options[:member]
- role = user.logged? ? Role.non_member : Role.anonymous
- if role.allowed_to?(permission)
- statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
- end
- end
- if user.logged?
- user.projects_by_role.each do |role, projects|
- if role.allowed_to?(permission) && projects.any?
- statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
- end
- end
- end
- if statement_by_role.empty?
- "1=0"
- else
- if block_given?
- statement_by_role.each do |role, statement|
- if s = yield(role, user)
- statement_by_role[role] = "(#{statement} AND (#{s}))"
- end
- end
- end
- "((#{base_statement}) AND (#{statement_by_role.values.join(' OR ')}))"
- end
- end
- end
-
- # Returns the Systemwide and project specific activities
- def activities(include_inactive=false)
- if include_inactive
- return all_activities
- else
- return active_activities
- end
- end
-
- # Will create a new Project specific Activity or update an existing one
- #
- # This will raise a ActiveRecord::Rollback if the TimeEntryActivity
- # does not successfully save.
- def update_or_create_time_entry_activity(id, activity_hash)
- if activity_hash.respond_to?(:has_key?) && activity_hash.has_key?('parent_id')
- self.create_time_entry_activity_if_needed(activity_hash)
- else
- activity = project.time_entry_activities.find_by_id(id.to_i)
- activity.update_attributes(activity_hash) if activity
- end
- end
-
- # Create a new TimeEntryActivity if it overrides a system TimeEntryActivity
- #
- # This will raise a ActiveRecord::Rollback if the TimeEntryActivity
- # does not successfully save.
- def create_time_entry_activity_if_needed(activity)
- if activity['parent_id']
-
- parent_activity = TimeEntryActivity.find(activity['parent_id'])
- activity['name'] = parent_activity.name
- activity['position'] = parent_activity.position
-
- if Enumeration.overridding_change?(activity, parent_activity)
- project_activity = self.time_entry_activities.create(activity)
-
- if project_activity.new_record?
- raise ActiveRecord::Rollback, "Overridding TimeEntryActivity was not successfully saved"
- else
- self.time_entries.update_all("activity_id = #{project_activity.id}", ["activity_id = ?", parent_activity.id])
- end
- end
- end
- end
-
- # Returns a :conditions SQL string that can be used to find the issues associated with this project.
- #
- # Examples:
- # project.project_condition(true) => "(projects.id = 1 OR (projects.lft > 1 AND projects.rgt < 10))"
- # project.project_condition(false) => "projects.id = 1"
- def project_condition(with_subprojects)
- cond = "#{Project.table_name}.id = #{id}"
- cond = "(#{cond} OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt}))" if with_subprojects
- cond
- end
-
- def self.find(*args)
- if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
- project = find_by_identifier(*args)
- raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
- project
- else
- super
- end
- end
-
- def self.find_by_param(*args)
- self.find(*args)
- end
-
- alias :base_reload :reload
- def reload(*args)
- @shared_versions = nil
- @rolled_up_versions = nil
- @rolled_up_trackers = nil
- @all_issue_custom_fields = nil
- @all_time_entry_custom_fields = nil
- @to_param = nil
- @allowed_parents = nil
- @allowed_permissions = nil
- @actions_allowed = nil
- @start_date = nil
- @due_date = nil
- base_reload(*args)
- end
-
- # def to_param
- # # id is used for projects with a numeric identifier (compatibility)
- # @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier)
- # end
-
- def active?
- self.status == STATUS_ACTIVE
- end
-
- def archived?
- self.status == STATUS_ARCHIVED
- end
-
- # Archives the project and its descendants
- def archive
- # Check that there is no issue of a non descendant project that is assigned
- # to one of the project or descendant versions
- v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten
- if v_ids.any? &&
- Issue.
- includes(:project).
- where("#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?", lft, rgt).
- where("#{Issue.table_name}.fixed_version_id IN (?)", v_ids).
- exists?
- return false
- end
- Project.transaction do
- archive!
- end
- true
- end
-
- # Unarchives the project
- # All its ancestors must be active
- def unarchive
- return false if ancestors.detect {|a| !a.active?}
- update_attribute :status, STATUS_ACTIVE
- end
-
- def close
- self_and_descendants.status(STATUS_ACTIVE).update_all :status => STATUS_CLOSED
- end
-
- def reopen
- self_and_descendants.status(STATUS_CLOSED).update_all :status => STATUS_ACTIVE
- end
-
- # Returns an array of projects the project can be moved to
- # by the current user
- def allowed_parents
- return @allowed_parents if @allowed_parents
- @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all
- @allowed_parents = @allowed_parents - self_and_descendants
- if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
- @allowed_parents << nil
- end
- unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent)
- @allowed_parents << parent
- end
- @allowed_parents
- end
-
- # Sets the parent of the project with authorization check
- def set_allowed_parent!(p)
- unless p.nil? || p.is_a?(Project)
- if p.to_s.blank?
- p = nil
- else
- p = Project.find_by_id(p)
- return false unless p
- end
- end
- if p.nil?
- if !new_record? && allowed_parents.empty?
- return false
- end
- elsif !allowed_parents.include?(p)
- return false
- end
- set_parent!(p)
- end
-
- # Sets the parent of the project
- # Argument can be either a Project, a String, a Fixnum or nil
- def set_parent!(p)
- unless p.nil? || p.is_a?(Project)
- if p.to_s.blank?
- p = nil
- else
- p = Project.find_by_id(p)
- return false unless p
- end
- end
- if p == parent && !p.nil?
- # Nothing to do
- true
- elsif p.nil? || (p.active? && move_possible?(p))
- set_or_update_position_under(p)
- Issue.update_versions_from_hierarchy_change(self)
- true
- else
- # Can not move to the given target
- false
- end
- end
-
- # Recalculates all lft and rgt values based on project names
- # Unlike Project.rebuild!, these values are recalculated even if the tree "looks" valid
- # Used in BuildProjectsTree migration
- def self.rebuild_tree!
- transaction do
- update_all "lft = NULL, rgt = NULL"
- rebuild!(false)
- end
- end
-
- # Returns an array of the trackers used by the project and its active sub projects
- def rolled_up_trackers
- @rolled_up_trackers ||=
- Tracker.
- joins(:projects).
- joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
- select("DISTINCT #{Tracker.table_name}.*").
- where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
- sorted.
- all
- end
-
- # Closes open and locked project versions that are completed
- def close_completed_versions
- Version.transaction do
- versions.where(:status => %w(open locked)).all.each do |version|
- if version.completed?
- version.update_attribute(:status, 'closed')
- end
- end
- end
- end
-
- # Returns a scope of the Versions on subprojects
- def rolled_up_versions
- @rolled_up_versions ||=
- Version.scoped(:include => :project,
- :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt])
- end
-
- # Returns a scope of the Versions used by the project
- def shared_versions
- if new_record?
- Version.scoped(:include => :project,
- :conditions => "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND #{Version.table_name}.sharing = 'system'")
- else
- @shared_versions ||= begin
- r = root? ? self : root
- Version.scoped(:include => :project,
- :conditions => "#{Project.table_name}.id = #{id}" +
- " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
- " #{Version.table_name}.sharing = 'system'" +
- " OR (#{Project.table_name}.lft >= #{r.lft} AND #{Project.table_name}.rgt <= #{r.rgt} AND #{Version.table_name}.sharing = 'tree')" +
- " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" +
- " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" +
- "))")
- end
- end
- end
-
- # Returns a hash of project users grouped by role
- def users_by_role
- members.includes(:user, :roles).all.inject({}) do |h, m|
- m.roles.each do |r|
- h[r] ||= []
- h[r] << m.user
- end
- h
- end
- end
-
- # Deletes all project's members
- def delete_all_members
- me, mr = Member.table_name, MemberRole.table_name
- connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
- Member.delete_all(['project_id = ?', id])
- end
-
- # Users/groups issues can be assigned to
- def assignable_users
- assignable = Setting.issue_group_assignment? ? member_principals : members
- assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort
- end
-
- # Returns the mail adresses of users that should be always notified on project events
- def recipients
- notified_users.collect {|user| user.mail}
- end
-
- # Returns the users that should be notified on project events
- def notified_users
- # TODO: User part should be extracted to User#notify_about?
- members.select {|m| m.principal.present? && (m.mail_notification? || m.principal.mail_notification == 'all')}.collect {|m| m.principal}
- end
-
- # Returns an array of all custom fields enabled for project issues
- # (explictly associated custom fields and custom fields enabled for all projects)
- def all_issue_custom_fields
- @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort
- end
-
- # Returns an array of all custom fields enabled for project time entries
- # (explictly associated custom fields and custom fields enabled for all projects)
- def all_time_entry_custom_fields
- @all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort
- end
-
- def project
- self
- end
-
- def <=>(project)
- name.downcase <=> project.name.downcase
- end
-
- def to_s
- name
- end
-
- # Returns a short description of the projects (first lines)
- def short_description(length = 255)
- #description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
- description.gsub(/<\/?.*?>/,"").html_safe if description
- end
-
- def css_classes
- s = 'project'
- s << ' root' if root?
- s << ' child' if child?
- s << (leaf? ? ' leaf' : ' parent')
- unless active?
- if archived?
- s << ' archived'
- else
- s << ' closed'
- end
- end
- s
- end
-
- # The earliest start date of a project, based on it's issues and versions
- def start_date
- @start_date ||= [
- issues.minimum('start_date'),
- shared_versions.minimum('effective_date'),
- Issue.fixed_version(shared_versions).minimum('start_date')
- ].compact.min
- end
-
- # The latest due date of an issue or version
- def due_date
- @due_date ||= [
- issues.maximum('due_date'),
- shared_versions.maximum('effective_date'),
- Issue.fixed_version(shared_versions).maximum('due_date')
- ].compact.max
- end
-
- def overdue?
- active? && !due_date.nil? && (due_date < Date.today)
- end
-
- # Returns the percent completed for this project, based on the
- # progress on it's versions.
- def completed_percent(options={:include_subprojects => false})
- if options.delete(:include_subprojects)
- total = self_and_descendants.collect(&:completed_percent).sum
-
- total / self_and_descendants.count
- else
- if versions.count > 0
- total = versions.collect(&:completed_percent).sum
-
- total / versions.count
- else
- 100
- end
- end
- end
-
- # Return true if this project allows to do the specified action.
- # action can be:
- # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
- # * a permission Symbol (eg. :edit_project)
- def allows_to?(action)
- if archived?
- # No action allowed on archived projects
- return false
- end
- unless active? || Redmine::AccessControl.read_action?(action)
- # No write action allowed on closed projects
- return false
- end
- # No action allowed on disabled modules
- if action.is_a? Hash
- allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
- else
- allowed_permissions.include? action
- end
- end
-
- def module_enabled?(module_name)
- module_name = module_name.to_s
- enabled_modules.detect {|m| m.name == module_name}
- end
-
- def enabled_module_names=(module_names)
- if module_names && module_names.is_a?(Array)
- module_names = module_names.collect(&:to_s).reject(&:blank?)
- self.enabled_modules = module_names.collect {|name| enabled_modules.detect {|mod| mod.name == name} || EnabledModule.new(:name => name)}
- else
- enabled_modules.clear
- end
- end
-
- # Returns an array of the enabled modules names
- def enabled_module_names
- enabled_modules.collect(&:name)
- end
-
- # Enable a specific module
- #
- # Examples:
- # project.enable_module!(:issue_tracking)
- # project.enable_module!("issue_tracking")
- def enable_module!(name)
- enabled_modules << EnabledModule.new(:name => name.to_s) unless module_enabled?(name)
- end
-
- # Disable a module if it exists
- #
- # Examples:
- # project.disable_module!(:issue_tracking)
- # project.disable_module!("issue_tracking")
- # project.disable_module!(project.enabled_modules.first)
- def disable_module!(target)
- target = enabled_modules.detect{|mod| target.to_s == mod.name} unless enabled_modules.include?(target)
- target.destroy unless target.blank?
- end
-
- safe_attributes 'name',
- 'description',
- 'homepage',
- 'is_public',
- 'hidden_repo',
- 'identifier',
- 'custom_field_values',
- 'custom_fields',
- 'tracker_ids',
- 'issue_custom_field_ids',
- 'project_type',
- 'dts_test',
- 'attachmenttype'
-
-
-
- safe_attributes 'enabled_module_names',
- :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
-
- safe_attributes 'inherit_members',
- :if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)}
-
- # Returns an array of projects that are in this project's hierarchy
- #
- # Example: parents, children, siblings
- def hierarchy
- parents = project.self_and_ancestors || []
- descendants = project.descendants || []
- project_hierarchy = parents | descendants # Set union
- end
-
- # Returns an auto-generated project identifier based on the last identifier used
- def self.next_identifier
- p = Project.order('created_on DESC').first
- p.nil? ? nil : p.identifier.to_s.succ
- end
-
- # Copies and saves the Project instance based on the +project+.
- # Duplicates the source project's:
- # * Wiki
- # * Versions
- # * Categories
- # * Issues
- # * Members
- # * Queries
- #
- # Accepts an +options+ argument to specify what to copy
- #
- # Examples:
- # project.copy(1) # => copies everything
- # project.copy(1, :only => 'members') # => copies members only
- # project.copy(1, :only => ['members', 'versions']) # => copies members and versions
- def copy(project, options={})
- project = project.is_a?(Project) ? project : Project.find(project)
-
- to_be_copied = %w(wiki versions issue_categories issues members queries boards)
- to_be_copied = to_be_copied & options[:only].to_a unless options[:only].nil?
-
- Project.transaction do
- if save
- reload
- to_be_copied.each do |name|
- send "copy_#{name}", project
- end
- Redmine::Hook.call_hook(:model_project_copy_before_save, :source_project => project, :destination_project => self)
- save
- end
- end
- end
-
- # Returns a new unsaved Project instance with attributes copied from +project+
- def self.copy_from(project)
- project = project.is_a?(Project) ? project : Project.find(project)
- # clear unique attributes
- attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt')
- copy = Project.new(attributes)
- copy.enabled_modules = project.enabled_modules
- copy.trackers = project.trackers
- copy.custom_values = project.custom_values.collect {|v| v.clone}
- copy.issue_custom_fields = project.issue_custom_fields
- copy
- end
-
- # Yields the given block for each project with its level in the tree
- def self.project_tree(projects, &block)
- ancestors = []
- projects.sort_by(&:lft).each do |project|
- while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
- ancestors.pop
- end
- yield project, ancestors.size
- ancestors << project
- end
- end
-
- private
-
- def after_parent_changed(parent_was)
- remove_inherited_member_roles
- add_inherited_member_roles
- end
-
- def update_inherited_members
- if parent
- if inherit_members? && !inherit_members_was
- remove_inherited_member_roles
- add_inherited_member_roles
- elsif !inherit_members? && inherit_members_was
- remove_inherited_member_roles
- end
- end
- end
-
- def remove_inherited_member_roles
- member_roles = memberships.map(&:member_roles).flatten
- member_role_ids = member_roles.map(&:id)
- member_roles.each do |member_role|
- if member_role.inherited_from && !member_role_ids.include?(member_role.inherited_from)
- member_role.destroy
- end
- end
- end
-
- def add_inherited_member_roles
- if inherit_members? && parent
- parent.memberships.each do |parent_member|
- member = Member.find_or_new(self.id, parent_member.user_id)
- parent_member.member_roles.each do |parent_member_role|
- member.member_roles << MemberRole.new(:role => parent_member_role.role, :inherited_from => parent_member_role.id)
- end
- member.save!
- end
- end
- end
-
- # Copies wiki from +project+
- def copy_wiki(project)
- # Check that the source project has a wiki first
- unless project.wiki.nil?
- wiki = self.wiki || Wiki.new
- wiki.attributes = project.wiki.attributes.dup.except("id", "project_id")
- wiki_pages_map = {}
- project.wiki.pages.each do |page|
- # Skip pages without content
- next if page.content.nil?
- new_wiki_content = WikiContent.new(page.content.attributes.dup.except("id", "page_id", "updated_on"))
- new_wiki_page = WikiPage.new(page.attributes.dup.except("id", "wiki_id", "created_on", "parent_id"))
- new_wiki_page.content = new_wiki_content
- wiki.pages << new_wiki_page
- wiki_pages_map[page.id] = new_wiki_page
- end
-
- self.wiki = wiki
- wiki.save
- # Reproduce page hierarchy
- project.wiki.pages.each do |page|
- if page.parent_id && wiki_pages_map[page.id]
- wiki_pages_map[page.id].parent = wiki_pages_map[page.parent_id]
- wiki_pages_map[page.id].save
- end
- end
- end
- end
-
- # Copies versions from +project+
- def copy_versions(project)
- project.versions.each do |version|
- new_version = Version.new
- new_version.attributes = version.attributes.dup.except("id", "project_id", "created_on", "updated_on")
- self.versions << new_version
- end
- end
-
- # Copies issue categories from +project+
- def copy_issue_categories(project)
- project.issue_categories.each do |issue_category|
- new_issue_category = IssueCategory.new
- new_issue_category.attributes = issue_category.attributes.dup.except("id", "project_id")
- self.issue_categories << new_issue_category
- end
- end
-
- # Copies issues from +project+
- def copy_issues(project)
- # Stores the source issue id as a key and the copied issues as the
- # value. Used to map the two togeather for issue relations.
- issues_map = {}
-
- # Store status and reopen locked/closed versions
- version_statuses = versions.reject(&:open?).map {|version| [version, version.status]}
- version_statuses.each do |version, status|
- version.update_attribute :status, 'open'
- end
-
- # Get issues sorted by root_id, lft so that parent issues
- # get copied before their children
- project.issues.reorder('root_id, lft').all.each do |issue|
- new_issue = Issue.new
- new_issue.copy_from(issue, :subtasks => false, :link => false)
- new_issue.project = self
- # Reassign fixed_versions by name, since names are unique per project
- if issue.fixed_version && issue.fixed_version.project == project
- new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name}
- end
- # Reassign the category by name, since names are unique per project
- if issue.category
- new_issue.category = self.issue_categories.detect {|c| c.name == issue.category.name}
- end
- # Parent issue
- if issue.parent_id
- if copied_parent = issues_map[issue.parent_id]
- new_issue.parent_issue_id = copied_parent.id
- end
- end
-
- self.issues << new_issue
- if new_issue.new_record?
- logger.info "Project#copy_issues: issue ##{issue.id} could not be copied: #{new_issue.errors.full_messages}" if logger && logger.info
- else
- issues_map[issue.id] = new_issue unless new_issue.new_record?
- end
- end
-
- # Restore locked/closed version statuses
- version_statuses.each do |version, status|
- version.update_attribute :status, status
- end
-
- # Relations after in case issues related each other
- project.issues.each do |issue|
- new_issue = issues_map[issue.id]
- unless new_issue
- # Issue was not copied
- next
- end
-
- # Relations
- issue.relations_from.each do |source_relation|
- new_issue_relation = IssueRelation.new
- new_issue_relation.attributes = source_relation.attributes.dup.except("id", "issue_from_id", "issue_to_id")
- new_issue_relation.issue_to = issues_map[source_relation.issue_to_id]
- if new_issue_relation.issue_to.nil? && Setting.cross_project_issue_relations?
- new_issue_relation.issue_to = source_relation.issue_to
- end
- new_issue.relations_from << new_issue_relation
- end
-
- issue.relations_to.each do |source_relation|
- new_issue_relation = IssueRelation.new
- new_issue_relation.attributes = source_relation.attributes.dup.except("id", "issue_from_id", "issue_to_id")
- new_issue_relation.issue_from = issues_map[source_relation.issue_from_id]
- if new_issue_relation.issue_from.nil? && Setting.cross_project_issue_relations?
- new_issue_relation.issue_from = source_relation.issue_from
- end
- new_issue.relations_to << new_issue_relation
- end
- end
- end
-
- # Copies members from +project+
- def copy_members(project)
- # Copy users first, then groups to handle members with inherited and given roles
- members_to_copy = []
- members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
- members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
-
- members_to_copy.each do |member|
- new_member = Member.new
- new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
- # only copy non inherited roles
- # inherited roles will be added when copying the group membership
- role_ids = member.member_roles.reject(&:inherited?).collect(&:role_id)
- next if role_ids.empty?
- new_member.role_ids = role_ids
- new_member.project = self
- self.members << new_member
- end
- end
-
- # Copies queries from +project+
- def copy_queries(project)
- project.queries.each do |query|
- new_query = IssueQuery.new
- new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
- new_query.sort_criteria = query.sort_criteria if query.sort_criteria
- new_query.project = self
- new_query.user_id = query.user_id
- self.queries << new_query
- end
- end
-
- # Copies boards from +project+
- def copy_boards(project)
- project.boards.each do |board|
- new_board = Board.new
- new_board.attributes = board.attributes.dup.except("id", "project_id", "topics_count", "messages_count", "last_message_id")
- new_board.project = self
- self.boards << new_board
- end
- end
-
- def allowed_permissions
- @allowed_permissions ||= begin
- module_names = enabled_modules.all(:select => :name).collect {|m| m.name}
- Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
- end
- end
-
- def allowed_actions
- @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
- end
-
- # Returns all the active Systemwide and project specific activities
- def active_activities
- overridden_activity_ids = self.time_entry_activities.collect(&:parent_id)
-
- if overridden_activity_ids.empty?
- return TimeEntryActivity.shared.active
- else
- return system_activities_and_project_overrides
- end
- end
-
- # Returns all the Systemwide and project specific activities
- # (inactive and active)
- def all_activities
- overridden_activity_ids = self.time_entry_activities.collect(&:parent_id)
-
- if overridden_activity_ids.empty?
- return TimeEntryActivity.shared
- else
- return system_activities_and_project_overrides(true)
- end
- end
-
- # Returns the systemwide active activities merged with the project specific overrides
- def system_activities_and_project_overrides(include_inactive=false)
- if include_inactive
- return TimeEntryActivity.shared.
- where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all +
- self.time_entry_activities
- else
- return TimeEntryActivity.shared.active.
- where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all +
- self.time_entry_activities.active
- end
- end
-
- # Archives subprojects recursively
- def archive!
- children.each do |subproject|
- subproject.send :archive!
- end
- update_attribute :status, STATUS_ARCHIVED
- end
-
- def update_position_under_parent
- set_or_update_position_under(parent)
- end
-
- def course
- @course
- end
-
- # Inserts/moves the project so that target's children or root projects stay alphabetically sorted
- def set_or_update_position_under(target_parent)
- parent_was = parent
- sibs = (target_parent.nil? ? self.class.roots : target_parent.children)
- to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
-
- if to_be_inserted_before
- move_to_left_of(to_be_inserted_before)
- elsif target_parent.nil?
- if sibs.empty?
- # move_to_root adds the project in first (ie. left) position
- move_to_root
- else
- move_to_right_of(sibs.last) unless self == sibs.last
- end
- else
- # move_to_child_of adds the project in last (ie.right) position
- move_to_child_of(target_parent)
- end
- if parent_was != target_parent
- after_parent_changed(parent_was)
- end
- end
-
- # 创建项目后在项目下同步创建一个讨论区
- def create_board_sync
- @board = self.boards.build
- self.name=" #{l(:label_borad_project) }"
- @board.name = self.name
- @board.description = self.name.to_s
- if @board.save
- logger.debug "[Project Model] ===> #{@board.to_json}"
- else
- logger.error "[Project Model] ===> Auto create board when Project saved, because #{@board.full_messages}"
- 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 Project < ActiveRecord::Base
+ include Redmine::SafeAttributes
+ ProjectType_project = 0
+ ProjectType_course = 1
+
+ # Project statuses
+ STATUS_ACTIVE = 1
+ STATUS_CLOSED = 5
+ STATUS_ARCHIVED = 9
+
+ # Maximum length for project identifiers
+ IDENTIFIER_MAX_LENGTH = 100
+
+ # Specific overidden Activities
+
+ belongs_to :homework_attach
+ has_many :time_entry_activities
+ has_many :members, :include => [:principal, :roles], :conditions => "#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}"
+ has_many :memberships, :class_name => 'Member'
+ has_many :member_principals, :class_name => 'Member',
+ :include => :principal,
+ :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{Principal::STATUS_ACTIVE})"
+ has_many :users, :through => :members
+ has_many :principals, :through => :member_principals, :source => :principal
+ has_many :enabled_modules, :dependent => :delete_all
+ has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
+ has_many :issues, :dependent => :destroy, :include => [:status, :tracker]
+ has_many :issue_changes, :through => :issues, :source => :journals
+ has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
+ has_many :time_entries, :dependent => :delete_all
+ has_many :queries, :class_name => 'IssueQuery', :dependent => :delete_all
+ has_many :documents, :dependent => :destroy
+ has_many :news, :dependent => :destroy, :include => :author
+ has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
+ has_many :boards, :dependent => :destroy, :order => "position ASC"
+ has_one :repository, :conditions => ["is_default = ?", true]
+ has_many :repositories, :dependent => :destroy
+ has_many :changesets, :through => :repository
+ #added by xianbo for delete biding_project
+ has_many :biding_projects, :dependent => :destroy
+ has_many :contesting_projects, :dependent => :destroy
+ has_many :softapplications, :through => :projecting_softapplications
+ #ended by xianbo
+ # added by fq
+ has_many :journals_for_messages, :as => :jour, :dependent => :destroy
+ #has_many :homework_for_courses, :dependent => :destroy
+ #has_many :homeworks, :through => :homework_for_courses, :source => :bid, :dependent => :destroy
+ has_many :shares, :dependent => :destroy
+ # has_many :students_for_courses, :dependent => :destroy
+ has_many :student, :through => :students_for_courses, :source => :user
+ has_one :course_extra, :class_name => 'Course', :foreign_key => :extra,:primary_key => :identifier, :dependent => :destroy
+ has_many :applied_projects
+
+
+ # end
+ #ADDED BY NIE
+ has_many :project_infos, :dependent => :destroy
+ has_one :project_status, :class_name => "ProjectStatus", :dependent => :destroy
+ has_many :user_grades, :class_name => "UserGrade", :dependent => :destroy
+ #end
+
+ has_one :wiki, :dependent => :destroy
+ ##added by xianbo
+ has_one :course, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
+ accepts_nested_attributes_for :course
+ ##end
+ # Custom field for the project issues
+ has_and_belongs_to_many :issue_custom_fields,
+ :class_name => 'IssueCustomField',
+ :order => "#{CustomField.table_name}.position",
+ :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
+ :association_foreign_key => 'custom_field_id'
+
+ has_many :tags, :through => :project_tags, :class_name => 'Tag'
+ has_many :project_tags, :class_name => 'ProjectTags'
+
+ # has_many :journals
+
+ acts_as_nested_set :order => 'name', :dependent => :destroy
+ acts_as_attachable :view_permission => :view_files,
+ :delete_permission => :manage_files
+
+ acts_as_customizable
+ acts_as_searchable :columns => ['name', 'identifier', 'description'], :project_key => 'id', :permission => nil
+ acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
+ :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o}},
+ :author => nil
+ ############################added by william
+ acts_as_taggable
+ scope :by_join_date, order("created_on DESC")
+ ###################added by liuping 关注
+ acts_as_watchable
+
+ attr_protected :status
+
+ validates_presence_of :name, :identifier
+ validates_uniqueness_of :identifier
+ validates_uniqueness_of :name
+ validates_associated :repository, :wiki
+ # validates_length_of :description, :maximum => 255
+ validates_length_of :name, :maximum => 255
+ validates_length_of :homepage, :maximum => 255
+ validates_length_of :identifier, :in => 1..IDENTIFIER_MAX_LENGTH
+ # donwcase letters, digits, dashes but not digits only
+ validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :if => Proc.new { |p| p.identifier_changed? }
+ # reserved words
+ validates_exclusion_of :identifier, :in => %w( new )
+
+ #此代码功能:为原redmine中项目的树形结构按名称首字母排序,本系统项目非树形结构,且项目排序方式无按首字母排序,另该代码执行会使空数据库时创建项目时出异常故注释掉
+ #after_save :update_position_under_parent, :if => Proc.new {|project| project.name_changed?}
+ after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?}
+ # 创建project之后默认创建一个board,之后的board去掉了board的概念
+ after_create :create_board_sync
+ before_destroy :delete_all_members
+ def remove_references_before_destroy
+ return if self.id.nil?
+ Watcher.delete_all ['watchable_id = ?', id]
+ end
+ scope :has_module, lambda {|mod|
+ where("#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s)
+ }
+ scope :active, lambda { where(:status => STATUS_ACTIVE) }
+ scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) }
+ scope :all_public, lambda { where(:is_public => true) }
+ scope :visible, lambda {|*args| where(Project.visible_condition(args.shift || User.current, *args)) }
+ scope :allowed_to, lambda {|*args|
+ user = User.current
+ permission = nil
+ if args.first.is_a?(Symbol)
+ permission = args.shift
+ else
+ user = args.shift
+ permission = args.shift
+ end
+ where(Project.allowed_to_condition(user, permission, *args))
+ }
+ scope :like, lambda {|arg|
+ if arg.blank?
+ where(nil)
+ else
+ pattern = "%#{arg.to_s.strip.downcase}%"
+ where("LOWER(name) LIKE :p ", :p => pattern)
+ end
+ }
+ scope :project_entities, -> { where(project_type: ProjectType_project) }
+ scope :course_entities, -> { where(project_type: ProjectType_course) }
+
+ def new_course
+ self.where('project_type = ?', 1)
+ end
+
+ # 获取项目的资源类型列表
+ def attachmenttypes
+ @attachmenttypes = Attachmentstype.find(:all, :conditions => ["#{Attachmentstype.table_name}.typeId= ?",self.attachmenttype ])
+ end
+
+ # 获取资源后缀名列表
+ def contenttypes
+ attachmenttypes
+ if @attachmenttypes.length >0
+ @attachmenttypes.last().suffixArr
+ end
+ end
+
+ #自定义验证
+ def validation
+ if !class_period.match([0-9])
+ errors.add_to_base("class period can only digital")
+ end
+ end
+
+# 项目留言 added by fq
+ def self.add_jour(user, notes)
+ project = Project.find('trustie')
+ # project.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => 0)
+ pjfm = project.journals_for_messages.build(:user_id => user.id, :notes => notes, :reply_id => 0)
+ pjfm.save
+ pjfm
+ end
+
+ def self.add_new_jour(user, notes, id, options={})
+ project = Project.find(id)
+ if options.count == 0
+ pjfm = project.journals_for_messages.build(:user_id => user.id, :notes => notes, :reply_id => 0)
+ else
+ pjfm = project.journals_for_messages.build(options)
+ end
+ pjfm.save
+ pjfm
+ end
+# end
+
+ # 管理员的邮件列表
+ def manager_recipients
+ notified = project.project_infos.collect(&:user)
+ notified.collect(&:mail)
+ end
+
+ def initialize(attributes=nil, *args)
+ super
+
+ initialized = (attributes || {}).stringify_keys
+ if !initialized.key?('identifier') && Setting.sequential_project_identifiers?
+ self.identifier = Project.next_identifier
+ end
+ if !initialized.key?('is_public')
+ self.is_public = Setting.default_projects_public?
+ end
+ if !initialized.key?('enabled_module_names')
+ self.enabled_module_names = Setting.default_projects_modules
+ end
+ if !initialized.key?('trackers') && !initialized.key?('tracker_ids')
+ default = Setting.default_projects_tracker_ids
+ if default.is_a?(Array)
+ self.trackers = Tracker.where(:id => default.map(&:to_i)).sorted.all
+ else
+ self.trackers = Tracker.sorted.all
+ end
+ end
+ end
+
+ def identifier=(identifier)
+ super unless identifier_frozen?
+ end
+
+ def identifier_frozen?
+ errors[:identifier].blank? && !(new_record? || identifier.blank?)
+ end
+
+ # returns latest created projects
+ # non public projects will be returned only if user is a member of those
+ def self.latest(user=nil, count=5)
+ visible(user).limit(count).order("created_on DESC").all
+ end
+
+ # Returns true if the project is visible to +user+ or to the current user.
+ def visible?(user=User.current)
+ user.allowed_to?(:view_project, self)
+ end
+
+ # Returns a SQL conditions string used to find all projects visible by the specified user.
+ #
+ # Examples:
+ # Project.visible_condition(admin) => "projects.status = 1"
+ # Project.visible_condition(normal_user) => "((projects.status = 1) AND (projects.is_public = 1 OR projects.id IN (1,3,4)))"
+ # Project.visible_condition(anonymous) => "((projects.status = 1) AND (projects.is_public = 1))"
+ def self.visible_condition(user, options={})
+ allowed_to_condition(user, :view_project, options)
+ end
+
+ # Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+
+ #
+ # Valid options:
+ # * :project => limit the condition to project
+ # * :with_subprojects => limit the condition to project and its subprojects
+ # * :member => limit the condition to the user projects
+ def self.allowed_to_condition(user, permission, options={})
+ perm = Redmine::AccessControl.permission(permission)
+ base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}")
+ if perm && perm.project_module
+ # If the permission belongs to a project module, make sure the module is enabled
+ base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')"
+ end
+ if options[:project]
+ project_statement = "#{Project.table_name}.id = #{options[:project].id}"
+ project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects]
+ base_statement = "(#{project_statement}) AND (#{base_statement})"
+ end
+
+ if user.admin?
+ base_statement
+ else
+ statement_by_role = {}
+ unless options[:member]
+ role = user.logged? ? Role.non_member : Role.anonymous
+ if role.allowed_to?(permission)
+ statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}"
+ end
+ end
+ if user.logged?
+ user.projects_by_role.each do |role, projects|
+ if role.allowed_to?(permission) && projects.any?
+ statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})"
+ end
+ end
+ end
+ if statement_by_role.empty?
+ "1=0"
+ else
+ if block_given?
+ statement_by_role.each do |role, statement|
+ if s = yield(role, user)
+ statement_by_role[role] = "(#{statement} AND (#{s}))"
+ end
+ end
+ end
+ "((#{base_statement}) AND (#{statement_by_role.values.join(' OR ')}))"
+ end
+ end
+ end
+
+ # Returns the Systemwide and project specific activities
+ def activities(include_inactive=false)
+ if include_inactive
+ return all_activities
+ else
+ return active_activities
+ end
+ end
+
+ # Will create a new Project specific Activity or update an existing one
+ #
+ # This will raise a ActiveRecord::Rollback if the TimeEntryActivity
+ # does not successfully save.
+ def update_or_create_time_entry_activity(id, activity_hash)
+ if activity_hash.respond_to?(:has_key?) && activity_hash.has_key?('parent_id')
+ self.create_time_entry_activity_if_needed(activity_hash)
+ else
+ activity = project.time_entry_activities.find_by_id(id.to_i)
+ activity.update_attributes(activity_hash) if activity
+ end
+ end
+
+ # Create a new TimeEntryActivity if it overrides a system TimeEntryActivity
+ #
+ # This will raise a ActiveRecord::Rollback if the TimeEntryActivity
+ # does not successfully save.
+ def create_time_entry_activity_if_needed(activity)
+ if activity['parent_id']
+
+ parent_activity = TimeEntryActivity.find(activity['parent_id'])
+ activity['name'] = parent_activity.name
+ activity['position'] = parent_activity.position
+
+ if Enumeration.overridding_change?(activity, parent_activity)
+ project_activity = self.time_entry_activities.create(activity)
+
+ if project_activity.new_record?
+ raise ActiveRecord::Rollback, "Overridding TimeEntryActivity was not successfully saved"
+ else
+ self.time_entries.update_all("activity_id = #{project_activity.id}", ["activity_id = ?", parent_activity.id])
+ end
+ end
+ end
+ end
+
+ # Returns a :conditions SQL string that can be used to find the issues associated with this project.
+ #
+ # Examples:
+ # project.project_condition(true) => "(projects.id = 1 OR (projects.lft > 1 AND projects.rgt < 10))"
+ # project.project_condition(false) => "projects.id = 1"
+ def project_condition(with_subprojects)
+ cond = "#{Project.table_name}.id = #{id}"
+ cond = "(#{cond} OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt}))" if with_subprojects
+ cond
+ end
+
+ def self.find(*args)
+ if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
+ project = find_by_identifier(*args)
+ raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
+ project
+ else
+ super
+ end
+ end
+
+ def self.find_by_param(*args)
+ self.find(*args)
+ end
+
+ alias :base_reload :reload
+ def reload(*args)
+ @shared_versions = nil
+ @rolled_up_versions = nil
+ @rolled_up_trackers = nil
+ @all_issue_custom_fields = nil
+ @all_time_entry_custom_fields = nil
+ @to_param = nil
+ @allowed_parents = nil
+ @allowed_permissions = nil
+ @actions_allowed = nil
+ @start_date = nil
+ @due_date = nil
+ base_reload(*args)
+ end
+
+ # def to_param
+ # # id is used for projects with a numeric identifier (compatibility)
+ # @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id.to_s : identifier)
+ # end
+
+ def active?
+ self.status == STATUS_ACTIVE
+ end
+
+ def archived?
+ self.status == STATUS_ARCHIVED
+ end
+
+ # Archives the project and its descendants
+ def archive
+ # Check that there is no issue of a non descendant project that is assigned
+ # to one of the project or descendant versions
+ v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten
+ if v_ids.any? &&
+ Issue.
+ includes(:project).
+ where("#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?", lft, rgt).
+ where("#{Issue.table_name}.fixed_version_id IN (?)", v_ids).
+ exists?
+ return false
+ end
+ Project.transaction do
+ archive!
+ end
+ true
+ end
+
+ # Unarchives the project
+ # All its ancestors must be active
+ def unarchive
+ return false if ancestors.detect {|a| !a.active?}
+ update_attribute :status, STATUS_ACTIVE
+ end
+
+ def close
+ self_and_descendants.status(STATUS_ACTIVE).update_all :status => STATUS_CLOSED
+ end
+
+ def reopen
+ self_and_descendants.status(STATUS_CLOSED).update_all :status => STATUS_ACTIVE
+ end
+
+ # Returns an array of projects the project can be moved to
+ # by the current user
+ def allowed_parents
+ return @allowed_parents if @allowed_parents
+ @allowed_parents = Project.where(Project.allowed_to_condition(User.current, :add_subprojects)).all
+ @allowed_parents = @allowed_parents - self_and_descendants
+ if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?)
+ @allowed_parents << nil
+ end
+ unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent)
+ @allowed_parents << parent
+ end
+ @allowed_parents
+ end
+
+ # Sets the parent of the project with authorization check
+ def set_allowed_parent!(p)
+ unless p.nil? || p.is_a?(Project)
+ if p.to_s.blank?
+ p = nil
+ else
+ p = Project.find_by_id(p)
+ return false unless p
+ end
+ end
+ if p.nil?
+ if !new_record? && allowed_parents.empty?
+ return false
+ end
+ elsif !allowed_parents.include?(p)
+ return false
+ end
+ set_parent!(p)
+ end
+
+ # Sets the parent of the project
+ # Argument can be either a Project, a String, a Fixnum or nil
+ def set_parent!(p)
+ unless p.nil? || p.is_a?(Project)
+ if p.to_s.blank?
+ p = nil
+ else
+ p = Project.find_by_id(p)
+ return false unless p
+ end
+ end
+ if p == parent && !p.nil?
+ # Nothing to do
+ true
+ elsif p.nil? || (p.active? && move_possible?(p))
+ set_or_update_position_under(p)
+ Issue.update_versions_from_hierarchy_change(self)
+ true
+ else
+ # Can not move to the given target
+ false
+ end
+ end
+
+ # Recalculates all lft and rgt values based on project names
+ # Unlike Project.rebuild!, these values are recalculated even if the tree "looks" valid
+ # Used in BuildProjectsTree migration
+ def self.rebuild_tree!
+ transaction do
+ update_all "lft = NULL, rgt = NULL"
+ rebuild!(false)
+ end
+ end
+
+ # Returns an array of the trackers used by the project and its active sub projects
+ def rolled_up_trackers
+ @rolled_up_trackers ||=
+ Tracker.
+ joins(:projects).
+ joins("JOIN #{EnabledModule.table_name} ON #{EnabledModule.table_name}.project_id = #{Project.table_name}.id AND #{EnabledModule.table_name}.name = 'issue_tracking'").
+ select("DISTINCT #{Tracker.table_name}.*").
+ where("#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt).
+ sorted.
+ all
+ end
+
+ # Closes open and locked project versions that are completed
+ def close_completed_versions
+ Version.transaction do
+ versions.where(:status => %w(open locked)).all.each do |version|
+ if version.completed?
+ version.update_attribute(:status, 'closed')
+ end
+ end
+ end
+ end
+
+ # Returns a scope of the Versions on subprojects
+ def rolled_up_versions
+ @rolled_up_versions ||=
+ Version.scoped(:include => :project,
+ :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt])
+ end
+
+ # Returns a scope of the Versions used by the project
+ def shared_versions
+ if new_record?
+ Version.scoped(:include => :project,
+ :conditions => "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND #{Version.table_name}.sharing = 'system'")
+ else
+ @shared_versions ||= begin
+ r = root? ? self : root
+ Version.scoped(:include => :project,
+ :conditions => "#{Project.table_name}.id = #{id}" +
+ " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
+ " #{Version.table_name}.sharing = 'system'" +
+ " OR (#{Project.table_name}.lft >= #{r.lft} AND #{Project.table_name}.rgt <= #{r.rgt} AND #{Version.table_name}.sharing = 'tree')" +
+ " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" +
+ " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" +
+ "))")
+ end
+ end
+ end
+
+ # Returns a hash of project users grouped by role
+ def users_by_role
+ members.includes(:user, :roles).all.inject({}) do |h, m|
+ m.roles.each do |r|
+ h[r] ||= []
+ h[r] << m.user
+ end
+ h
+ end
+ end
+
+ # Deletes all project's members
+ def delete_all_members
+ me, mr = Member.table_name, MemberRole.table_name
+ connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})")
+ Member.delete_all(['project_id = ?', id])
+ end
+
+ # Users/groups issues can be assigned to
+ def assignable_users
+ assignable = Setting.issue_group_assignment? ? member_principals : members
+ assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort
+ end
+
+ # Returns the mail adresses of users that should be always notified on project events
+ def recipients
+ notified_users.collect {|user| user.mail}
+ end
+
+ # Returns the users that should be notified on project events
+ def notified_users
+ # TODO: User part should be extracted to User#notify_about?
+ members.select {|m| m.principal.present? && (m.mail_notification? || m.principal.mail_notification == 'all')}.collect {|m| m.principal}
+ end
+
+ # Returns an array of all custom fields enabled for project issues
+ # (explictly associated custom fields and custom fields enabled for all projects)
+ def all_issue_custom_fields
+ @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort
+ end
+
+ # Returns an array of all custom fields enabled for project time entries
+ # (explictly associated custom fields and custom fields enabled for all projects)
+ def all_time_entry_custom_fields
+ @all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort
+ end
+
+ def project
+ self
+ end
+
+ def <=>(project)
+ name.downcase <=> project.name.downcase
+ end
+
+ def to_s
+ name
+ end
+
+ # Returns a short description of the projects (first lines)
+ def short_description(length = 255)
+ #description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
+ description.gsub(/<\/?.*?>/,"").html_safe if description
+ end
+
+ def css_classes
+ s = 'project'
+ s << ' root' if root?
+ s << ' child' if child?
+ s << (leaf? ? ' leaf' : ' parent')
+ unless active?
+ if archived?
+ s << ' archived'
+ else
+ s << ' closed'
+ end
+ end
+ s
+ end
+
+ # The earliest start date of a project, based on it's issues and versions
+ def start_date
+ @start_date ||= [
+ issues.minimum('start_date'),
+ shared_versions.minimum('effective_date'),
+ Issue.fixed_version(shared_versions).minimum('start_date')
+ ].compact.min
+ end
+
+ # The latest due date of an issue or version
+ def due_date
+ @due_date ||= [
+ issues.maximum('due_date'),
+ shared_versions.maximum('effective_date'),
+ Issue.fixed_version(shared_versions).maximum('due_date')
+ ].compact.max
+ end
+
+ def overdue?
+ active? && !due_date.nil? && (due_date < Date.today)
+ end
+
+ # Returns the percent completed for this project, based on the
+ # progress on it's versions.
+ def completed_percent(options={:include_subprojects => false})
+ if options.delete(:include_subprojects)
+ total = self_and_descendants.collect(&:completed_percent).sum
+
+ total / self_and_descendants.count
+ else
+ if versions.count > 0
+ total = versions.collect(&:completed_percent).sum
+
+ total / versions.count
+ else
+ 100
+ end
+ end
+ end
+
+ # Return true if this project allows to do the specified action.
+ # action can be:
+ # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
+ # * a permission Symbol (eg. :edit_project)
+ def allows_to?(action)
+ if archived?
+ # No action allowed on archived projects
+ return false
+ end
+ unless active? || Redmine::AccessControl.read_action?(action)
+ # No write action allowed on closed projects
+ return false
+ end
+ # No action allowed on disabled modules
+ if action.is_a? Hash
+ allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
+ else
+ allowed_permissions.include? action
+ end
+ end
+
+ def module_enabled?(module_name)
+ module_name = module_name.to_s
+ enabled_modules.detect {|m| m.name == module_name}
+ end
+
+ def enabled_module_names=(module_names)
+ if module_names && module_names.is_a?(Array)
+ module_names = module_names.collect(&:to_s).reject(&:blank?)
+ self.enabled_modules = module_names.collect {|name| enabled_modules.detect {|mod| mod.name == name} || EnabledModule.new(:name => name)}
+ else
+ enabled_modules.clear
+ end
+ end
+
+ # Returns an array of the enabled modules names
+ def enabled_module_names
+ enabled_modules.collect(&:name)
+ end
+
+ # Enable a specific module
+ #
+ # Examples:
+ # project.enable_module!(:issue_tracking)
+ # project.enable_module!("issue_tracking")
+ def enable_module!(name)
+ enabled_modules << EnabledModule.new(:name => name.to_s) unless module_enabled?(name)
+ end
+
+ # Disable a module if it exists
+ #
+ # Examples:
+ # project.disable_module!(:issue_tracking)
+ # project.disable_module!("issue_tracking")
+ # project.disable_module!(project.enabled_modules.first)
+ def disable_module!(target)
+ target = enabled_modules.detect{|mod| target.to_s == mod.name} unless enabled_modules.include?(target)
+ target.destroy unless target.blank?
+ end
+
+ safe_attributes 'name',
+ 'description',
+ 'homepage',
+ 'is_public',
+ 'hidden_repo',
+ 'identifier',
+ 'custom_field_values',
+ 'custom_fields',
+ 'tracker_ids',
+ 'issue_custom_field_ids',
+ 'project_type',
+ 'dts_test',
+ 'attachmenttype'
+
+
+
+ safe_attributes 'enabled_module_names',
+ :if => lambda {|project, user| project.new_record? || user.allowed_to?(:select_project_modules, project) }
+
+ safe_attributes 'inherit_members',
+ :if => lambda {|project, user| project.parent.nil? || project.parent.visible?(user)}
+
+ # Returns an array of projects that are in this project's hierarchy
+ #
+ # Example: parents, children, siblings
+ def hierarchy
+ parents = project.self_and_ancestors || []
+ descendants = project.descendants || []
+ project_hierarchy = parents | descendants # Set union
+ end
+
+ # Returns an auto-generated project identifier based on the last identifier used
+ def self.next_identifier
+ p = Project.order('created_on DESC').first
+ p.nil? ? nil : p.identifier.to_s.succ
+ end
+
+ # Copies and saves the Project instance based on the +project+.
+ # Duplicates the source project's:
+ # * Wiki
+ # * Versions
+ # * Categories
+ # * Issues
+ # * Members
+ # * Queries
+ #
+ # Accepts an +options+ argument to specify what to copy
+ #
+ # Examples:
+ # project.copy(1) # => copies everything
+ # project.copy(1, :only => 'members') # => copies members only
+ # project.copy(1, :only => ['members', 'versions']) # => copies members and versions
+ def copy(project, options={})
+ project = project.is_a?(Project) ? project : Project.find(project)
+
+ to_be_copied = %w(wiki versions issue_categories issues members queries boards)
+ to_be_copied = to_be_copied & options[:only].to_a unless options[:only].nil?
+
+ Project.transaction do
+ if save
+ reload
+ to_be_copied.each do |name|
+ send "copy_#{name}", project
+ end
+ Redmine::Hook.call_hook(:model_project_copy_before_save, :source_project => project, :destination_project => self)
+ save
+ end
+ end
+ end
+
+ # Returns a new unsaved Project instance with attributes copied from +project+
+ def self.copy_from(project)
+ project = project.is_a?(Project) ? project : Project.find(project)
+ # clear unique attributes
+ attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt')
+ copy = Project.new(attributes)
+ copy.enabled_modules = project.enabled_modules
+ copy.trackers = project.trackers
+ copy.custom_values = project.custom_values.collect {|v| v.clone}
+ copy.issue_custom_fields = project.issue_custom_fields
+ copy
+ end
+
+ # Yields the given block for each project with its level in the tree
+ def self.project_tree(projects, &block)
+ ancestors = []
+ projects.sort_by(&:lft).each do |project|
+ while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
+ ancestors.pop
+ end
+ yield project, ancestors.size
+ ancestors << project
+ end
+ end
+
+ private
+
+ def after_parent_changed(parent_was)
+ remove_inherited_member_roles
+ add_inherited_member_roles
+ end
+
+ def update_inherited_members
+ if parent
+ if inherit_members? && !inherit_members_was
+ remove_inherited_member_roles
+ add_inherited_member_roles
+ elsif !inherit_members? && inherit_members_was
+ remove_inherited_member_roles
+ end
+ end
+ end
+
+ def remove_inherited_member_roles
+ member_roles = memberships.map(&:member_roles).flatten
+ member_role_ids = member_roles.map(&:id)
+ member_roles.each do |member_role|
+ if member_role.inherited_from && !member_role_ids.include?(member_role.inherited_from)
+ member_role.destroy
+ end
+ end
+ end
+
+ def add_inherited_member_roles
+ if inherit_members? && parent
+ parent.memberships.each do |parent_member|
+ member = Member.find_or_new(self.id, parent_member.user_id)
+ parent_member.member_roles.each do |parent_member_role|
+ member.member_roles << MemberRole.new(:role => parent_member_role.role, :inherited_from => parent_member_role.id)
+ end
+ member.save!
+ end
+ end
+ end
+
+ # Copies wiki from +project+
+ def copy_wiki(project)
+ # Check that the source project has a wiki first
+ unless project.wiki.nil?
+ wiki = self.wiki || Wiki.new
+ wiki.attributes = project.wiki.attributes.dup.except("id", "project_id")
+ wiki_pages_map = {}
+ project.wiki.pages.each do |page|
+ # Skip pages without content
+ next if page.content.nil?
+ new_wiki_content = WikiContent.new(page.content.attributes.dup.except("id", "page_id", "updated_on"))
+ new_wiki_page = WikiPage.new(page.attributes.dup.except("id", "wiki_id", "created_on", "parent_id"))
+ new_wiki_page.content = new_wiki_content
+ wiki.pages << new_wiki_page
+ wiki_pages_map[page.id] = new_wiki_page
+ end
+
+ self.wiki = wiki
+ wiki.save
+ # Reproduce page hierarchy
+ project.wiki.pages.each do |page|
+ if page.parent_id && wiki_pages_map[page.id]
+ wiki_pages_map[page.id].parent = wiki_pages_map[page.parent_id]
+ wiki_pages_map[page.id].save
+ end
+ end
+ end
+ end
+
+ # Copies versions from +project+
+ def copy_versions(project)
+ project.versions.each do |version|
+ new_version = Version.new
+ new_version.attributes = version.attributes.dup.except("id", "project_id", "created_on", "updated_on")
+ self.versions << new_version
+ end
+ end
+
+ # Copies issue categories from +project+
+ def copy_issue_categories(project)
+ project.issue_categories.each do |issue_category|
+ new_issue_category = IssueCategory.new
+ new_issue_category.attributes = issue_category.attributes.dup.except("id", "project_id")
+ self.issue_categories << new_issue_category
+ end
+ end
+
+ # Copies issues from +project+
+ def copy_issues(project)
+ # Stores the source issue id as a key and the copied issues as the
+ # value. Used to map the two togeather for issue relations.
+ issues_map = {}
+
+ # Store status and reopen locked/closed versions
+ version_statuses = versions.reject(&:open?).map {|version| [version, version.status]}
+ version_statuses.each do |version, status|
+ version.update_attribute :status, 'open'
+ end
+
+ # Get issues sorted by root_id, lft so that parent issues
+ # get copied before their children
+ project.issues.reorder('root_id, lft').all.each do |issue|
+ new_issue = Issue.new
+ new_issue.copy_from(issue, :subtasks => false, :link => false)
+ new_issue.project = self
+ # Reassign fixed_versions by name, since names are unique per project
+ if issue.fixed_version && issue.fixed_version.project == project
+ new_issue.fixed_version = self.versions.detect {|v| v.name == issue.fixed_version.name}
+ end
+ # Reassign the category by name, since names are unique per project
+ if issue.category
+ new_issue.category = self.issue_categories.detect {|c| c.name == issue.category.name}
+ end
+ # Parent issue
+ if issue.parent_id
+ if copied_parent = issues_map[issue.parent_id]
+ new_issue.parent_issue_id = copied_parent.id
+ end
+ end
+
+ self.issues << new_issue
+ if new_issue.new_record?
+ logger.info "Project#copy_issues: issue ##{issue.id} could not be copied: #{new_issue.errors.full_messages}" if logger && logger.info
+ else
+ issues_map[issue.id] = new_issue unless new_issue.new_record?
+ end
+ end
+
+ # Restore locked/closed version statuses
+ version_statuses.each do |version, status|
+ version.update_attribute :status, status
+ end
+
+ # Relations after in case issues related each other
+ project.issues.each do |issue|
+ new_issue = issues_map[issue.id]
+ unless new_issue
+ # Issue was not copied
+ next
+ end
+
+ # Relations
+ issue.relations_from.each do |source_relation|
+ new_issue_relation = IssueRelation.new
+ new_issue_relation.attributes = source_relation.attributes.dup.except("id", "issue_from_id", "issue_to_id")
+ new_issue_relation.issue_to = issues_map[source_relation.issue_to_id]
+ if new_issue_relation.issue_to.nil? && Setting.cross_project_issue_relations?
+ new_issue_relation.issue_to = source_relation.issue_to
+ end
+ new_issue.relations_from << new_issue_relation
+ end
+
+ issue.relations_to.each do |source_relation|
+ new_issue_relation = IssueRelation.new
+ new_issue_relation.attributes = source_relation.attributes.dup.except("id", "issue_from_id", "issue_to_id")
+ new_issue_relation.issue_from = issues_map[source_relation.issue_from_id]
+ if new_issue_relation.issue_from.nil? && Setting.cross_project_issue_relations?
+ new_issue_relation.issue_from = source_relation.issue_from
+ end
+ new_issue.relations_to << new_issue_relation
+ end
+ end
+ end
+
+ # Copies members from +project+
+ def copy_members(project)
+ # Copy users first, then groups to handle members with inherited and given roles
+ members_to_copy = []
+ members_to_copy += project.memberships.select {|m| m.principal.is_a?(User)}
+ members_to_copy += project.memberships.select {|m| !m.principal.is_a?(User)}
+
+ members_to_copy.each do |member|
+ new_member = Member.new
+ new_member.attributes = member.attributes.dup.except("id", "project_id", "created_on")
+ # only copy non inherited roles
+ # inherited roles will be added when copying the group membership
+ role_ids = member.member_roles.reject(&:inherited?).collect(&:role_id)
+ next if role_ids.empty?
+ new_member.role_ids = role_ids
+ new_member.project = self
+ self.members << new_member
+ end
+ end
+
+ # Copies queries from +project+
+ def copy_queries(project)
+ project.queries.each do |query|
+ new_query = IssueQuery.new
+ new_query.attributes = query.attributes.dup.except("id", "project_id", "sort_criteria")
+ new_query.sort_criteria = query.sort_criteria if query.sort_criteria
+ new_query.project = self
+ new_query.user_id = query.user_id
+ self.queries << new_query
+ end
+ end
+
+ # Copies boards from +project+
+ def copy_boards(project)
+ project.boards.each do |board|
+ new_board = Board.new
+ new_board.attributes = board.attributes.dup.except("id", "project_id", "topics_count", "messages_count", "last_message_id")
+ new_board.project = self
+ self.boards << new_board
+ end
+ end
+
+ def allowed_permissions
+ @allowed_permissions ||= begin
+ module_names = enabled_modules.all(:select => :name).collect {|m| m.name}
+ Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
+ end
+ end
+
+ def allowed_actions
+ @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
+ end
+
+ # Returns all the active Systemwide and project specific activities
+ def active_activities
+ overridden_activity_ids = self.time_entry_activities.collect(&:parent_id)
+
+ if overridden_activity_ids.empty?
+ return TimeEntryActivity.shared.active
+ else
+ return system_activities_and_project_overrides
+ end
+ end
+
+ # Returns all the Systemwide and project specific activities
+ # (inactive and active)
+ def all_activities
+ overridden_activity_ids = self.time_entry_activities.collect(&:parent_id)
+
+ if overridden_activity_ids.empty?
+ return TimeEntryActivity.shared
+ else
+ return system_activities_and_project_overrides(true)
+ end
+ end
+
+ # Returns the systemwide active activities merged with the project specific overrides
+ def system_activities_and_project_overrides(include_inactive=false)
+ if include_inactive
+ return TimeEntryActivity.shared.
+ where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all +
+ self.time_entry_activities
+ else
+ return TimeEntryActivity.shared.active.
+ where("id NOT IN (?)", self.time_entry_activities.collect(&:parent_id)).all +
+ self.time_entry_activities.active
+ end
+ end
+
+ # Archives subprojects recursively
+ def archive!
+ children.each do |subproject|
+ subproject.send :archive!
+ end
+ update_attribute :status, STATUS_ARCHIVED
+ end
+
+ def update_position_under_parent
+ set_or_update_position_under(parent)
+ end
+
+ def course
+ @course
+ end
+
+ # Inserts/moves the project so that target's children or root projects stay alphabetically sorted
+ def set_or_update_position_under(target_parent)
+ parent_was = parent
+ sibs = (target_parent.nil? ? self.class.roots : target_parent.children)
+ to_be_inserted_before = sibs.sort_by {|c| c.name.to_s.downcase}.detect {|c| c.name.to_s.downcase > name.to_s.downcase }
+
+ if to_be_inserted_before
+ move_to_left_of(to_be_inserted_before)
+ elsif target_parent.nil?
+ if sibs.empty?
+ # move_to_root adds the project in first (ie. left) position
+ move_to_root
+ else
+ move_to_right_of(sibs.last) unless self == sibs.last
+ end
+ else
+ # move_to_child_of adds the project in last (ie.right) position
+ move_to_child_of(target_parent)
+ end
+ if parent_was != target_parent
+ after_parent_changed(parent_was)
+ end
+ end
+
+ # 创建项目后在项目下同步创建一个讨论区
+ def create_board_sync
+ @board = self.boards.build
+ self.name=" #{l(:label_borad_project) }"
+ @board.name = self.name
+ @board.description = self.name.to_s
+ if @board.save
+ logger.debug "[Project Model] ===> #{@board.to_json}"
+ else
+ logger.error "[Project Model] ===> Auto create board when Project saved, because #{@board.full_messages}"
+ end
+ end
+
+
+end
diff --git a/app/models/project_status.rb b/app/models/project_status.rb
index 7fd246234..d9e6159c3 100644
--- a/app/models/project_status.rb
+++ b/app/models/project_status.rb
@@ -1,21 +1,21 @@
-class ProjectStatus < ActiveRecord::Base
- attr_accessible :changesets_count, :watchers_count, :project_id, :project_type,:grade
- belongs_to :project
- belongs_to :watchers
- belongs_to :changesets
- validates_presence_of :project_id
- validates_uniqueness_of :project_id
-
- scope :visible, lambda {|*args| nil }
- # 更新字段 watchers_count 加1 这里没有做用户是否存在的匹配
- # 负责这个表的聂同学 是在新建用户时就新建了该表的记录
- # 但是 如果超级用户删除其他用户的话会造成读取错误 这里是遗漏点
- # 删除用户时 此表创建人员未作相应删除动作
- def update_watchers_count(num)
- if self.watchers_count||0 >= 0
- self.update_attribute(:watchers_count, self.watchers_count.to_i + num)
- end
- end
-
-
-end
+class ProjectStatus < ActiveRecord::Base
+ attr_accessible :changesets_count, :watchers_count, :project_id, :project_type,:grade
+ belongs_to :project
+ belongs_to :watchers
+ belongs_to :changesets
+ validates_presence_of :project_id
+ validates_uniqueness_of :project_id
+
+ scope :visible, lambda {|*args| nil }
+ # 更新字段 watchers_count 加1 这里没有做用户是否存在的匹配
+ # 负责这个表的聂同学 是在新建用户时就新建了该表的记录
+ # 但是 如果超级用户删除其他用户的话会造成读取错误 这里是遗漏点
+ # 删除用户时 此表创建人员未作相应删除动作
+ def update_watchers_count(num)
+ if self.watchers_count||0 >= 0
+ self.update_attribute(:watchers_count, self.watchers_count.to_i + num)
+ end
+ end
+
+
+end
diff --git a/app/views/account/password_recovery.html.erb b/app/views/account/password_recovery.html.erb
index ff455f325..568d4d4bb 100644
--- a/app/views/account/password_recovery.html.erb
+++ b/app/views/account/password_recovery.html.erb
@@ -12,9 +12,9 @@
- <%=l(:field_password_confirmation)%> *
- <%= password_field_tag 'new_password_confirmation', nil, :size => 25 %>
-
+ <%= l(:field_password_confirmation)%> *
+ <%= password_field_tag 'new_password_confirmation', nil, :size => 25 %>
+
<%= submit_tag l(:button_save) %>
<% end %>
diff --git a/app/views/attachments/_form.html.erb b/app/views/attachments/_form.html.erb
index 81dc5d939..5c22ff44c 100644
--- a/app/views/attachments/_form.html.erb
+++ b/app/views/attachments/_form.html.erb
@@ -1,52 +1,52 @@
-
-<% if defined?(container) && container && container.saved_attachments %>
- <% container.attachments.each_with_index do |attachment, i| %>
-
- <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
- <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") +
- if attachment.id.nil?
- else
- link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload')
- end
- %>
- <%#= render :partial => 'tags/tag', :locals => {:obj => attachment, :object_flag => "6"} %>
- <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public, :class => 'is_public')%>
- <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
-
- <% end %>
-<% end %>
-
-
-
-<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
-
-<%= button_tag "浏览", :type=>"button", :onclick=>"_file.click()",:onmouseover => 'this.focus()' %>
-<%= 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)
- } %>
-<%= l(:label_no_file_uploaded)%>
-(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
-
-
-<% content_for :header_tags do %>
- <%= javascript_include_tag 'attachments' %>
-<% end %>
-
-
+
+<% if defined?(container) && container && container.saved_attachments %>
+ <% container.attachments.each_with_index do |attachment, i| %>
+
+ <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
+ <%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") +
+ if attachment.id.nil?
+ else
+ link_to(' '.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload')
+ end
+ %>
+ <%#= render :partial => 'tags/tag', :locals => {:obj => attachment, :object_flag => "6"} %>
+ <%= check_box_tag("attachments[p#{i}][is_public_checkbox]", attachment.is_public, :class => 'is_public')%>
+ <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
+
+ <% end %>
+<% end %>
+
+
+
+<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
+
+<%= button_tag "浏览", :type=>"button", :onclick=>"_file.click()",:onmouseover => 'this.focus()' %>
+<%= 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)
+ } %>
+<%= l(:label_no_file_uploaded)%>
+(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
+
+
+<% content_for :header_tags do %>
+ <%= javascript_include_tag 'attachments' %>
+<% end %>
+
+
diff --git a/app/views/attachments/file.html.erb b/app/views/attachments/file.html.erb
index d4cb19880..75fa1858c 100644
--- a/app/views/attachments/file.html.erb
+++ b/app/views/attachments/file.html.erb
@@ -1,26 +1,26 @@
-<%=h @attachment.filename %>
-
-
-
<%= h("#{@attachment.description} - ") unless @attachment.description.blank? %>
- <%= link_to_user(@attachment.author) %>, <%= format_time(@attachment.created_on) %>
-
<%= link_to_attachment @attachment, :text => l(:button_download), :download => true -%>
- (<%= number_to_human_size @attachment.filesize %>)    
-
- <% if @attachment!=nil &&(@attachment.container_type == 'Document' || @attachment.container_type == 'WikiPage') &&
- User.current.allowed_to?({:controller => 'code_review', :action => 'update_diff_view'}, @attachment.project) %>
- <%= l(:review_assignments)+":" %><%= link = link_to(l(:button_add), {:controller => 'code_review',
- :action => 'assign', :action_type => 'attachment',
- :id=>@attachment.project,
- :change_id => '', :attachment_id => @attachment.id,
- }, :class => 'icon icon-add') %>
- <% end %>
-
-
-
-<%= render :partial => 'common/file', :locals => {:content => @content, :filename => @attachment.filename} %>
-
-<% html_title @attachment.filename %>
-
-<% content_for :header_tags do -%>
- <%= stylesheet_link_tag "scm" -%>
-<% end -%>
+<%=h @attachment.filename %>
+
+
+
<%= h("#{@attachment.description} - ") unless @attachment.description.blank? %>
+ <%= link_to_user(@attachment.author) %>, <%= format_time(@attachment.created_on) %>
+
<%= link_to_attachment @attachment, :text => l(:button_download), :download => true -%>
+ (<%= number_to_human_size @attachment.filesize %>)    
+
+ <% if @attachment!=nil &&(@attachment.container_type == 'Document' || @attachment.container_type == 'WikiPage') &&
+ User.current.allowed_to?({:controller => 'code_review', :action => 'update_diff_view'}, @attachment.project) %>
+ <%= l(:review_assignments)+":" %><%= link = link_to(l(:button_add), {:controller => 'code_review',
+ :action => 'assign', :action_type => 'attachment',
+ :id=>@attachment.project,
+ :change_id => '', :attachment_id => @attachment.id,
+ }, :class => 'icon icon-add') %>
+ <% end %>
+
+
+
+<%= render :partial => 'common/file', :locals => {:content => @content, :filename => @attachment.filename} %>
+
+<% html_title @attachment.filename %>
+
+<% content_for :header_tags do -%>
+ <%= stylesheet_link_tag "scm" -%>
+<% end -%>
diff --git a/app/views/boards/_project_show.html.erb b/app/views/boards/_project_show.html.erb
index a14e32d6d..199155838 100644
--- a/app/views/boards/_project_show.html.erb
+++ b/app/views/boards/_project_show.html.erb
@@ -67,12 +67,15 @@
<% @topics.each do |topic| %>
- <%= link_to image_tag(url_to_avatar(topic.author), :class => "avatar"), user_path(topic.author) %>
+
+ <%= link_to image_tag(url_to_avatar(topic.author), :class => "avatar"), user_path(topic.author) %>
+
<%= link_to h(topic.subject.truncate(40,ommision:'...')), board_message_path(@board, topic),title:topic.subject.to_s %>
+ <%= topic.locked? ? 'locked' : '' %>">
+ <%= link_to h(topic.subject.truncate(40,ommision:'...')), board_message_path(@board, topic),title:topic.subject.to_s %>
diff --git a/app/views/contest_notification/show.html.erb b/app/views/contest_notification/show.html.erb
index e04a18c68..5664d5398 100644
--- a/app/views/contest_notification/show.html.erb
+++ b/app/views/contest_notification/show.html.erb
@@ -9,8 +9,12 @@
- contest.trustie.net
- <%=link_to l(:field_homepage), home_path %> > <%#=link_to l(:label_contest_innovate), :controller => 'welcome', :action => 'contest' %> 详情
+
+ contest.trustie.net
+
+
+ <%=link_to l(:field_homepage), home_path %> >
+ <%=link_to l(:label_contest_innovate), welcome_contest_path %> > 详情
diff --git a/app/views/contestnotifications/_form.html.erb b/app/views/contestnotifications/_form.html.erb
index afcb60657..36bcd78c6 100644
--- a/app/views/contestnotifications/_form.html.erb
+++ b/app/views/contestnotifications/_form.html.erb
@@ -3,8 +3,23 @@
<%= l(:bale_news_notice) %>
-
<%= f.text_field :title, :required => true, :size => 60,:maxlength => 60, :style => "width:488px;" %>
-
<%= f.text_area :description, :required => true, :cols => 60, :rows => 11, :class => 'wiki-edit', :style => "width:490px;" %>
+
+ <%= f.text_field :title,
+ :required => true,
+ :size => 60,
+ :maxlength => 60,
+ :style => "width:488px;"
+ %>
+
+
+ <%= f.text_area :description,
+ :required => true,
+ :cols => 60,
+ :rows => 11,
+ :class => 'wiki-edit',
+ :style => "width:490px;"
+ %>
+
diff --git a/app/views/contestnotifications/_news.html.erb b/app/views/contestnotifications/_news.html.erb
index fbac2557f..c8af85cf4 100644
--- a/app/views/contestnotifications/_news.html.erb
+++ b/app/views/contestnotifications/_news.html.erb
@@ -2,24 +2,47 @@
<%#= link_to h(news.title), news_path(news) %>
<%#= "(#{l(:label_x_comments, :count => news.comments_count)})" if news.comments_count > 0 %>
- <%# unless news.summary.blank? %> <%#=h news.summary %> <% end %>
+ <%# unless news.summary.blank? %><%#=h news.summary %>
<%#= authoring news.created_on, news.author %>
-->
-
+
+
+
- <%=link_to contestnotifications.author,contest_contestnotification_path(contestnotifications)%>
- <%= l(:label_project_newshare) %> <%= link_to h(contestnotifications.title), contest_contestnotification_path(contestnotifications) %>
+
+
+ <%=link_to contestnotifications.author,
+ contest_contestnotification_path(contestnotifications)
+ %>
+
+
+ <%= l(:label_project_newshare) %>
+
+ <%= link_to h(contestnotifications.title),
+ contest_contestnotification_path(contestnotifications)
+ %>
- <%=h contestnotifications.description%>
+
+
+ <%=h contestnotifications.description%>
+
+
- <%= contestnotifications.created_at %>
- <%= link_to l(:label_project_newother),contest_contestnotification_path(contestnotifications)%>
+
+
+ <%= contestnotifications.created_at %>
+
+
+
+ <%= link_to l(:label_project_newother),
+ contest_contestnotification_path(contestnotifications)
+ %>
<%= "(#{l(:label_x_comments, :count => contestnotifications.notificationcomments_count)})" if contestnotifications.notificationcomments_count > 0 %>
diff --git a/app/views/contestnotifications/edit.html.erb b/app/views/contestnotifications/edit.html.erb
index 7b91d922c..1606d97d6 100644
--- a/app/views/contestnotifications/edit.html.erb
+++ b/app/views/contestnotifications/edit.html.erb
@@ -1,9 +1,15 @@
<%=l(:label_news)%>
-<%= labelled_form_for @contestnotification, url: contest_contestnotification_path, :html => { :id => 'contestnotifications-form', :multipart => true, :method => :put } do |f| %>
+<%= labelled_form_for @contestnotification,
+ :url => contest_contestnotification_path,
+ :html => { :id => 'contestnotifications-form',
+ :multipart => true,
+ :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
-<%= preview_link preview_contestnotifications_path(id: @contestnotification), 'contestnotifications-form' %>
+<%= preview_link preview_contestnotifications_path(id: @contestnotification),
+ 'contestnotifications-form'
+%>
<% end %>
diff --git a/app/views/contestnotifications/new.html.erb b/app/views/contestnotifications/new.html.erb
index 30d5f0106..a5a1c7e58 100644
--- a/app/views/contestnotifications/new.html.erb
+++ b/app/views/contestnotifications/new.html.erb
@@ -1,5 +1,7 @@
-<%= labelled_form_for @contestnotification, :url => contest_contestnotifications_path(@contest), :html => { :id => 'contestnotifications-form', :multipart => true } do |f| %>
+<%= labelled_form_for @contestnotification,
+ :url => contest_contestnotifications_path(@contest),
+ :html => { :id => 'contestnotifications-form', :multipart => true } do |f| %>
<%= render :partial => 'contestnotifications/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create), :class => "whiteButton m3p10 h30" %>
<%= submit_tag l(:button_cancel), :class => "whiteButton m3p10 h30",:onclick => "cancel();" %>
diff --git a/app/views/contestnotifications/show.html.erb b/app/views/contestnotifications/show.html.erb
index b6ac88008..a44ee5909 100644
--- a/app/views/contestnotifications/show.html.erb
+++ b/app/views/contestnotifications/show.html.erb
@@ -1,21 +1,26 @@
<%= link_to(l(:button_edit),
- edit_contest_contestnotification_path(@contest, @contestnotification),
- :class => 'icon icon-edit',
- :accesskey => accesskey(:edit),
- :onclick => '$("#edit-contestnotifications").show(); return true;') if (User.current.admin? && User.current.logged? )||(User.current == @contest.author && User.current.logged?)%>
+ edit_contest_contestnotification_path(@contest, @contestnotification),
+ :class => 'icon icon-edit',
+ :accesskey => accesskey(:edit),
+ :onclick => '$("#edit-contestnotifications").show(); return true;') if (User.current.admin? && User.current.logged? )||(User.current == @contest.author && User.current.logged?)%>
<%= delete_link contest_contestnotification_path(@contest, @contestnotification) if (User.current.admin? && User.current.logged? )||(User.current == @contest.author && User.current.logged?) %>
<%=h @contestnotification.title %>
- <%= labelled_form_for @contestnotification, :url => contest_contestnotification_path(@contest),
- :html => { :id => 'contestnotifications-form', :multipart => true, :method => :put } do |f| %>
+ <%= labelled_form_for @contestnotification,
+ :url => contest_contestnotification_path(@contest),
+ :html => { :id => 'contestnotifications-form',
+ :multipart => true,
+ :method => :put } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
- <%= link_to l(:button_cancel), "#", :onclick => '$("#edit-contestnotifications").hide(); return false;' %>
+ <%= link_to l(:button_cancel),
+ "#",
+ :onclick => '$("#edit-contestnotifications").hide(); return false;' %>
<% end %>
@@ -23,7 +28,9 @@
<% end %>
diff --git a/app/views/contests/new_contest.html.erb b/app/views/contests/new_contest.html.erb
index 7366b4203..019c8c1db 100644
--- a/app/views/contests/new_contest.html.erb
+++ b/app/views/contests/new_contest.html.erb
@@ -1,10 +1,12 @@
<%=l(:label_newtype_contest)%>
-<%= labelled_form_for @contest, :url => {:controller => 'contests', :action => 'create_contest'}, method: :post do |f| %>
+<%= labelled_form_for @contest,
+ :url => {:controller => 'contests', :action => 'create_contest'},
+ method: :post do |f| %>
- <%= render :partial => 'form_contest', :locals => { :f => f } %>
- <%= submit_tag l(:button_create) %>
- <%= javascript_tag "$('#bid_name').focus();" %>
- <% end %>
+ <%= render :partial => 'form_contest', :locals => { :f => f } %>
+ <%= submit_tag l(:button_create) %>
+ <%= javascript_tag "$('#bid_name').focus();" %>
+ <% end %>
\ No newline at end of file
diff --git a/app/views/contests/set_reward_project.js.erb b/app/views/contests/set_reward_project.js.erb
index c0de3e2ef..2ea06abb1 100644
--- a/app/views/contests/set_reward_project.js.erb
+++ b/app/views/contests/set_reward_project.js.erb
@@ -1,26 +1,27 @@
+
$('#reward_result_<%= @contesting_project_id %>').html('<%= j(
-if get_prize(@c_p).nil? or get_prize(@c_p) == ""
+if get_prize(@c_p).nil? || get_prize(@c_p) == ""
if @contest.deadline < Date.today
puts '未评奖'
end
else
case get_prize(@c_p)
- when '-1'
- image_tag("/images/bid/special_reward.png")
- when '0'
- image_tag("/images/bid/first_reward.png")
- when '1'
- image_tag("/images/bid/second_reward.png")
- when '2'
- image_tag("/images/bid/third_reward.png")
- when '3'
- image_tag("/images/bid/forth_reward.png")
- when '4'
- image_tag("/images/bid/fifth_reward.png")
- when '5'
- image_tag("/images/bid/qualified.png")
+ when '-1'
+ image_tag("/images/bid/special_reward.png")
+ when '0'
+ image_tag("/images/bid/first_reward.png")
+ when '1'
+ image_tag("/images/bid/second_reward.png")
+ when '2'
+ image_tag("/images/bid/third_reward.png")
+ when '3'
+ image_tag("/images/bid/forth_reward.png")
+ when '4'
+ image_tag("/images/bid/fifth_reward.png")
+ when '5'
+ image_tag("/images/bid/qualified.png")
end
end
diff --git a/app/views/contests/set_reward_softapplication.js.erb b/app/views/contests/set_reward_softapplication.js.erb
index a8cf0d486..2906d95d3 100644
--- a/app/views/contests/set_reward_softapplication.js.erb
+++ b/app/views/contests/set_reward_softapplication.js.erb
@@ -1,26 +1,28 @@
+
$('#reward_result_<%= @contesting_softapplication_id %>').html('<%= j(
-if get_prize(@c_sa).nil? or get_prize(@c_sa) == ""
+
+if get_prize(@c_sa).nil? || get_prize(@c_sa) == ""
if @contest.deadline < Date.today
puts '未评奖'
end
else
case get_prize(@c_sa)
- when '-1'
- image_tag("/images/bid/special_reward.png")
- when '0'
- image_tag("/images/bid/first_reward.png")
- when '1'
- image_tag("/images/bid/second_reward.png")
- when '2'
- image_tag("/images/bid/third_reward.png")
- when '3'
- image_tag("/images/bid/forth_reward.png")
- when '4'
- image_tag("/images/bid/fifth_reward.png")
- when '5'
- image_tag("/images/bid/qualified.png")
+ when '-1'
+ image_tag("/images/bid/special_reward.png")
+ when '0'
+ image_tag("/images/bid/first_reward.png")
+ when '1'
+ image_tag("/images/bid/second_reward.png")
+ when '2'
+ image_tag("/images/bid/third_reward.png")
+ when '3'
+ image_tag("/images/bid/forth_reward.png")
+ when '4'
+ image_tag("/images/bid/fifth_reward.png")
+ when '5'
+ image_tag("/images/bid/qualified.png")
end
end
diff --git a/app/views/contests/settings.html.erb b/app/views/contests/settings.html.erb
index a2780f060..4f8b8b0f2 100644
--- a/app/views/contests/settings.html.erb
+++ b/app/views/contests/settings.html.erb
@@ -1,6 +1,7 @@
-
<%=l(:label_contest_settings)%>
+ <%=l(:label_contest_settings)%>
+
diff --git a/app/views/contests/show_attendingcontest.html.erb b/app/views/contests/show_attendingcontest.html.erb
index 799656f36..3ba3e73ba 100644
--- a/app/views/contests/show_attendingcontest.html.erb
+++ b/app/views/contests/show_attendingcontest.html.erb
@@ -79,18 +79,35 @@
<%= render_flash_messages %>
-
<%= l(:label_wellmeaning_intimation_contentone) %>
-
1) <%= l(:label_wellmeaning_intimation_contenttwo) %>
-
2) <%= l(:label_wellmeaning_intimation_contentthree) %>
+
+ <%= l(:label_wellmeaning_intimation_contentone) %>
+
+
+ 1) <%= l(:label_wellmeaning_intimation_contenttwo) %>
+
+
+ 2) <%= l(:label_wellmeaning_intimation_contentthree) %>
+
<% if User.current.logged? %>
<%= l(:label_attending_contest) %>:
- <%= link_to l(:label_new_attendingcontest_work), "javascript:void(0);", onclick: "$('#put-project-form').slideToggle();" %>
+
+ <%= link_to l(:label_new_attendingcontest_work),
+ "javascript:void(0);",
+ onclick: "$('#put-project-form').slideToggle();"
+ %>
+
+
+
+
+ <%= render "new_softapplication" %>
+
+
<% else %>
<%= l(:label_user_login_attending_contest) %>
@@ -98,10 +115,7 @@
<% end %>
-
-
- <%= render "new_softapplication" %>
-
+
diff --git a/app/views/contests/show_contest.html.erb b/app/views/contests/show_contest.html.erb
index 121f5e690..11937d2bd 100644
--- a/app/views/contests/show_contest.html.erb
+++ b/app/views/contests/show_contest.html.erb
@@ -2,9 +2,18 @@
<%= image_tag(url_to_avatar(@contest.author), :class => "avatar")%>
- <%= link_to(@contest.author.lastname+@contest.author.firstname, user_path(@contest.author))%>:<%= @contest.name %>
+
+ <%= link_to(@contest.author.lastname+@contest.author.firstname,
+ user_path(@contest.author))
+ %>
+ :<%= @contest.name %>
- <%= l(:label_bids_reward_method) %><%= @contest.budget%>
+
+ <%= l(:label_bids_reward_method) %>
+
+ <%= @contest.budget%>
+
+
<%= @contest.description %>
@@ -15,7 +24,11 @@
<%= render :partial => "/praise_tread/praise_tread",
- :locals => {:obj => @contest,:show_flag => true,:user_id =>User.current.id,:horizontal => false}%>
+ :locals => {:obj => @contest,
+ :show_flag => true,
+ :user_id =>User.current.id,
+ :horizontal => false}
+ %>
@@ -23,7 +36,9 @@
- <%= render :partial => 'history', :locals => { :contest => @contest, :journals => @jour, :state => false} %>
+ <%= render :partial => 'history',
+ :locals => { :contest => @contest, :journals => @jour, :state => false}
+ %>