diff --git a/Gemfile b/Gemfile index 3027c16cd..0125e9d60 100644 --- a/Gemfile +++ b/Gemfile @@ -30,11 +30,21 @@ gem 'rails_kindeditor',path:'lib/rails_kindeditor' #gem "rmagick", ">= 2.0.0" gem 'binding_of_caller' gem 'chinese_pinyin' +# gem 'sunspot_rails', '~> 1.3.3' +# gem 'sunspot_solr' +# gem 'sunspot' +# gem 'progress_bar' +gem 'ansi' + +gem 'kaminari' +gem 'elasticsearch-model' +gem 'elasticsearch-rails' group :development do gem 'grape-swagger' gem 'better_errors', '~> 1.1.0' gem 'rack-mini-profiler', '~> 0.9.3' + gem 'win32console' end group :development, :test do diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index cd1de16fc..0c61c449e 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -374,6 +374,7 @@ class CoursesController < ApplicationController def settings if User.current.allowed_to?(:as_teacher,@course) + @select_tab = params[:tab] @issue_custom_fields = IssueCustomField.sorted.all @issue_category ||= IssueCategory.new @member ||= @course.members.new diff --git a/app/controllers/exercise_controller.rb b/app/controllers/exercise_controller.rb index 6d6d429b6..a99ab32ec 100644 --- a/app/controllers/exercise_controller.rb +++ b/app/controllers/exercise_controller.rb @@ -6,6 +6,18 @@ class ExerciseController < ApplicationController include ExerciseHelper def index + publish_exercises = Exercise.where("publish_time is not null and exercise_status = 1 and publish_time <=?",Time.now) + publish_exercises.each do |exercise| + exercise.update_column('exercise_status', 2) + course = exercise.course + course.members.each do |m| + exercise.course_messages << CourseMessage.new(:user_id => m.user_id, :course_id => course.id, :viewed => false, :status => 2) + end + end + end_exercises = Exercise.where("end_time <=? and exercise_status = 2",Time.now) + end_exercises.each do |exercise| + exercise.update_column('exercise_status', 3) + end if @course.is_public == 0 && !User.current.member_of_course?(@course) render_403 return @@ -24,6 +36,18 @@ class ExerciseController < ApplicationController end def show + publish_exercises = Exercise.where("publish_time is not null and exercise_status = 1 and publish_time <=?",Time.now) + publish_exercises.each do |exercise| + exercise.update_column('exercise_status', 2) + course = exercise.course + course.members.each do |m| + exercise.course_messages << CourseMessage.new(:user_id => m.user_id, :course_id => course.id, :viewed => false, :status => 2) + end + end + end_exercises = Exercise.where("end_time <=? and exercise_status = 2",Time.now) + end_exercises.each do |exercise| + exercise.update_column('exercise_status', 3) + end unless User.current.member_of_course?(@course) render_403 return @@ -316,6 +340,9 @@ class ExerciseController < ApplicationController @exercise.exercise_status = 2 @exercise.publish_time = Time.now if @exercise.save + @exercise.course.members.each do |m| + @exercise.course_messages << CourseMessage.create(:user_id =>m.user_id, :course_id => @exercise.course.id, :viewed => false,:status=>2) + end #redirect_to exercise_index_url(:course_id=> @course.id) respond_to do |format| format.js @@ -331,6 +358,7 @@ class ExerciseController < ApplicationController @exercise.exercise_questions.each do |exercise_question| exercise_question.exercise_answers.destroy_all end + @exercise.course_messages.destroy_all @exercise.exercise_users.destroy_all @exercise.exercise_status = 1 @exercise.publish_time = nil @@ -484,6 +512,10 @@ class ExerciseController < ApplicationController @exercise.update_attributes(:show_result => params[:show_result]) @exercise.update_attributes(:exercise_status => 2) @exercise.update_attributes(:publish_time => Time.now) + course = @exercise.course + course.members.each do |m| + @exercise.course_messages << CourseMessage.new(:user_id => m.user_id, :course_id => course.id, :viewed => false, :status => 2) + end redirect_to exercise_url(@exercise) return elsif @exercise.publish_time > Time.now diff --git a/app/controllers/memos_controller.rb b/app/controllers/memos_controller.rb index 4181090a5..2d2c058d4 100644 --- a/app/controllers/memos_controller.rb +++ b/app/controllers/memos_controller.rb @@ -73,6 +73,7 @@ class MemosController < ApplicationController end end #end + format.js format.html { redirect_to back_memo_url, notice: "#{l :label_memo_create_succ}" } format.json { render json: @memo, status: :created, location: @memo } else @@ -152,6 +153,7 @@ class MemosController < ApplicationController end def update + @flag = false respond_to do |format| if( #@memo.update_column(:subject, params[:memo][:subject]) && @memo.update_column(:content, params[:memo][:content]) && @@ -159,10 +161,12 @@ class MemosController < ApplicationController @memo.update_column(:lock, params[:memo][:lock]) && @memo.update_column(:subject,params[:memo][:subject])) @memo.save_attachments(params[:attachments] || (params[:memo] && params[:memo][:uploads])) - @memo.save + @flag = @memo.save # @memo.root.update_attribute(:updated_at, @memo.updated_at) + format.js format.html {redirect_to back_memo_url, notice: "#{l :label_memo_create_succ}"} else + format.js format.html { render action: "edit" } format.json { render json: @person.errors, status: :unprocessable_entity } end diff --git a/app/controllers/org_subfields_controller.rb b/app/controllers/org_subfields_controller.rb index da20b6fcf..6fdf0ae40 100644 --- a/app/controllers/org_subfields_controller.rb +++ b/app/controllers/org_subfields_controller.rb @@ -14,6 +14,7 @@ class OrgSubfieldsController < ApplicationController def update @subfield = OrgSubfield.find(params[:id]) + @organization = Organization.find(@subfield.organization_id) @subfield.update_attributes(:name => params[:name]) end end diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index 85f57917b..47a0930a5 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -94,7 +94,11 @@ class OrganizationsController < ApplicationController end @page = params[:page] respond_to do |format| - format.html + format.html { + if !params[:show_homepage].nil? + render :layout => 'base_organization' + end + } format.js end else @@ -102,6 +106,13 @@ class OrganizationsController < ApplicationController end end + def homepage + @organization = Organization.find(params[:id]) + respond_to do |format| + format.html {render :layout => 'base_organization'} + end + end + def update @organization = Organization.find(params[:id]) @organization.name = params[:organization][:name] diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 2bd54954a..c1e124063 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -64,32 +64,53 @@ class RepositoriesController < ApplicationController end def forked - # 被forked的标识如果不满足单个用户唯一性,则不执行fork - if is_sigle_identifier?(User.current, @repository.identifier) - # REDO: 那些人有权限forked项目 - g = Gitlab.client - gproject = g.post ("/projects/fork/#{@project.gpid}?user_id=#{User.current.gid}") - if gproject - copy_project(@project, gproject) - end + # 如果当前用户已经fork过该项目,不会新fork项目,则跳至已fork的项 + unless has_forked?(@project, User.current) + project = project_from_current_project(@project.id, User.current.id) + redirect_to project_path(project) else - flash[:notice] = l(:project_gitlab_fork_double_message) - redirect_to settings_project_url(@project, :tab => 'repositories') + # 单个用户只能拥有一个名字一样的版本库,否则不能fork + # if is_sigle_identifier?(User.current, @repository.identifier) + # REDO: 那些人有权限forked项目 + g = Gitlab.client + gproject = g.fork(@project.gpid, User.current.gid) + if gproject + copy_project(@project, gproject) + forked_count = @project.forked_count.to_i + 1 + @project.update_attributes(:forked_count => forked_count) + end + # else + # flash[:notice] = l(:project_gitlab_fork_double_message) + # redirect_to settings_project_url(@project, :tab => 'repositories') + # end end + + end + + # 判断用户是否已经fork过该项目 + def has_forked?(project, user) + projects = Project.where("user_id =?", user) + projects.map(&:forked_from_project_id).detect{|s| s == @project.id}.nil? ? true : false + end + + # 获取当前用户fork过的项目 + def project_from_current_project(project, user) + project = Project.where("user_id =? and forked_from_project_id =?",user, project).first end # copy a project for fork - def copy_project(project, gproject) + def copy_project(tproject, gproject) project = Project.new - project.name = @project.name - project.is_public = @project.is_public - project.status = @project.status - project.description = @project.description - project.hidden_repo = @project.hidden_repo + project.name = tproject.name + project.is_public = tproject.is_public + project.status = tproject.status + project.description = tproject.description + project.hidden_repo = tproject.hidden_repo project.user_id = User.current.id project.project_type = 0 - project.project_new_type = @project.project_new_type + project.project_new_type = tproject.project_new_type project.gpid = gproject.id + project.forked_from_project_id = tproject.id if project.save r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first m = Member.new(:user => User.current, :roles => [r]) @@ -124,16 +145,16 @@ class RepositoriesController < ApplicationController def copy_repository(project, gproject) # 避免 - if is_sigle_identifier?(project.user_id, gproject.name) + # if is_sigle_identifier?(project.user_id, gproject.name) repository = Repository.factory('Git') repository.project_id = project.id repository.type = 'Repository::Gitlab' repository.url = gproject.name repository.identifier = gproject.name repository = repository.save - else - flash[:notice] = l(:project_gitlab_create_double_message) - end + # else + # flash[:notice] = l(:project_gitlab_create_double_message) + # end end def newrepo diff --git a/app/controllers/student_work_controller.rb b/app/controllers/student_work_controller.rb index d2aba1386..5e71b0114 100644 --- a/app/controllers/student_work_controller.rb +++ b/app/controllers/student_work_controller.rb @@ -3,8 +3,8 @@ class StudentWorkController < ApplicationController include StudentWorkHelper require 'bigdecimal' require "base64" - before_filter :find_homework, :only => [:new, :index, :create, :student_work_absence_penalty, :absence_penalty_list, :evaluation_list, :program_test,:set_score_rule,:forbidden_anonymous_comment] - before_filter :find_work, :only => [:edit, :update, :show, :destroy, :add_score, :praise_student_work] + before_filter :find_homework, :only => [:new, :index, :create, :student_work_absence_penalty, :absence_penalty_list, :evaluation_list, :program_test,:set_score_rule,:forbidden_anonymous_comment,:delete_work] + before_filter :find_work, :only => [:edit, :update, :show, :destroy, :add_score, :praise_student_work,:retry_work,:revise_attachment] before_filter :member_of_course, :only => [:index, :new, :create, :show, :add_score, :praise_student_work] before_filter :author_of_work, :only => [:edit, :update, :destroy] before_filter :teacher_of_course, :only => [:student_work_absence_penalty, :absence_penalty_list, :evaluation_list, :set_score_rule, :forbidden_anonymous_comment] @@ -222,7 +222,7 @@ class StudentWorkController < ApplicationController def edit @user = User.current - if !User.current.admin? && @homework.homework_type == 2 #编程作业不能修改作业 + if (!User.current.admin? && @homework.homework_type == 2) || Time.parse(@homework.end_time.to_s).strftime("%Y-%m-%d") < Time.now.strftime("%Y-%m-%d") #编程作业不能修改作业|| 截止日期已到不能修改作业 render_403 else respond_to do |format| @@ -284,6 +284,23 @@ class StudentWorkController < ApplicationController end end + def delete_work + @work = StudentWork.where("user_id =? and homework_common_id =?", User.current.id, @homework.id).first + if @work + @work.destroy + end + redirect_to user_homeworks_user_path(User.current.id) + end + + def retry_work + if @work.destroy + @student_work = StudentWork.new + respond_to do |format| + format.js + end + end + end + #添加评分,已评分则为修改评分 def add_score @is_last = params[:is_last] == "true" @@ -510,6 +527,18 @@ class StudentWorkController < ApplicationController @course_activity = params[:course_activity].to_i end + def revise_attachment + Attachment.attach_filesex(@work, params[:attachments], params[:attachment_type]) + revise_attachments = @work.attachments.where("attachtype = 7").reorder("created_on desc") + if revise_attachments.count == 2 + revise_attachments.last.destroy + end + #@attachment = @work.attachments.where("attachtype = 7").order("created_on desc").first + respond_to do |format| + format.js + end + end + private def hsd_committed_work?(user, homework) sw = StudentWork.where("user_id =? and homework_common_id =?", user, homework).first diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 776e6b7d5..834009d71 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -92,7 +92,7 @@ class UsersController < ApplicationController end # 用户消息 - # 说明: homework 发布作业;message:讨论区; news:新闻; poll:问卷;works_reviewers:作品评阅;works_reply:作品回复 + # 说明: homework 发布作业;message:讨论区; news:新闻; poll:问卷;works_reviewers:作品评阅;works_reply:作品回复,exercise:课程测验 # issue:问题;journal:缺陷状态更新; forum:公共贴吧: user_feedback: 用户留言; new_reply:新闻回复(comment) def user_messages if !User.current.logged? @@ -128,7 +128,7 @@ class UsersController < ApplicationController #课程相关消息 when 'homework' - @message_alls = CourseMessage.where("course_message_type in ('HomeworkCommon','StudentWorksScore','JournalsForMessage','StudentWork') and user_id =?", @user).order("created_at desc") + @message_alls = CourseMessage.where("course_message_type in ('HomeworkCommon','StudentWorksScore','JournalsForMessage','StudentWork','Exercise') and user_id =?", @user).order("created_at desc") when 'course_message' @message_alls = CourseMessage.where("course_message_type =? and user_id =?", "Message", @user).order("created_at desc") when 'course_news' @@ -1367,7 +1367,7 @@ class UsersController < ApplicationController end end - if(params[:type].blank? || params[:type] == "1") #全部 + if(params[:type].nil? || params[:type].blank? || params[:type] == "1" || params[:type] == 'all') #全部 if User.current.id.to_i == params[:id].to_i user_course_ids = User.current.courses.map { |c| c.id} #我的资源库的话,那么应该是我上传的所有资源 加上 我加入的课程的所有资源 @attachments = Attachment.where("(author_id = #{params[:id]} and container_type in('Project','Principal','Course','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+ @@ -1429,6 +1429,7 @@ class UsersController < ApplicationController @course = @user.courses .select { |course| @user.allowed_to?(:as_teacher,course)} end + @search = params[:search] #这里仅仅是传递需要发送的资源id @send_id = params[:send_id] @send_ids = params[:checkbox1] || params[:send_ids] @@ -1445,6 +1446,7 @@ class UsersController < ApplicationController else @projects = @user.projects end + @search = params[:search] #这里仅仅是传递需要发送的资源id @send_id = params[:send_id] @send_ids = params[:checkbox1] || params[:send_ids] #搜索的时候 和 直接 用表格提交的时候的send_ids @@ -1856,7 +1858,7 @@ class UsersController < ApplicationController # 根据资源关键字进行搜索 def resource_search search = params[:search].to_s.strip.downcase - if(params[:type].nil? || params[:type] == "1") #全部 + if(params[:type].nil? || params[:type].blank? || params[:type] == "1" || params[:type] == 'all') #全部 if User.current.id.to_i == params[:id].to_i user_course_ids = User.current.courses.map { |c| c.id} #我的资源库的话,那么应该是我上传的所有资源 加上 我加入的课程的所有资源 取交集并查询 @attachments = Attachment.where("((author_id = #{params[:id]} and container_type in('Project','Principal','Course','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+ diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 784066378..815a44839 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -151,36 +151,104 @@ class WelcomeController < ApplicationController end def search - search_condition = params[:q] - search_type = params[:search_type].to_sym unless search_condition.blank? - search_by = params[:search_by] + @name = params[:q] + @search_type = params[:search_type] + case params[:search_type] + when 'all' + @alls = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + operator: "or", + fields: ['login', 'firstname','lastname','name','description^0.5','filename'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).page(params[:page] || 1).per(20).results + when 'user' + @users = User.search(@name).page(params[:page] || 1).per(20) + when 'project' + @projects = Project.search(@name).page(params[:page] || 1).per(20).results + when 'course' + @courses = Course.search(@name).page(params[:page] || 1).per(20).results + when 'attachment' + @attachments = Attachment.search(@name).page(params[:page] || 1).per(20).results + else + @alls = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + operator: "or", + fields: ['login', 'firstname','lastname','name','description^0.5','filename'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).page(params[:page] || 1).per(20).results - if search_type.nil? && params[:contests_search] && params[:name] != "" - search_type = :contests - search_condition = params[:name] end + @users_count = User.search(@name).results.total + + @course_count = Course.search(@name).results.total + @attach_count = Attachment.search(@name).results.total + @project_count = Project.search(@name).results.total + @total_count = Elasticsearch::Model.search({ + query: { + multi_match: { + query: @name, + type:"most_fields", + operator: "or", + fields: ['login', 'firstname','lastname','name','description^0.5','filename'] + } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {}, + name:{}, + description:{}, + filename:{} + } + } + },[User,Course,Attachment,Project] ).results.total + # search_type = params[:search_type].to_sym unless search_condition.blank? + # search_by = params[:search_by] + # + # 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_url(:name => search_condition, - :project_type => Project::ProjectType_project) - when :courses - redirect_to courses_search_url(:name => search_condition) - when :contests - redirect_to contests_url(:name => search_condition) - when :users - redirect_to users_search_url(:name => search_condition,:search_by => search_by) - when :users_teacher - redirect_to users_search_url(:name => search_condition, :search_by => search_by, :role => :teacher) - when :users_student - redirect_to users_search_url(:name => search_condition, :search_by => search_by, :role => :student) - else - #redirect_to home_path, :alert => l(:label_sumbit_empty) - (redirect_to signin_path, :notice => l(:label_sumbit_empty);return) #if params[:name].blank? - end - } + format.js + format.html{ render :layout=>'users_base'} end end diff --git a/app/controllers/words_controller.rb b/app/controllers/words_controller.rb index 6cc622c49..c9c4dcce3 100644 --- a/app/controllers/words_controller.rb +++ b/app/controllers/words_controller.rb @@ -3,6 +3,8 @@ class WordsController < ApplicationController include ApplicationHelper before_filter :find_user, :only => [:new, :create, :destroy, :more, :back] + before_filter :require_login, :only => [:create_reply] + def create if params[:new_form][:user_message].size>0 && User.current.logged? unless params[:user_id].nil? diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 046e0c5a0..cbc646eac 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1869,6 +1869,23 @@ module ApplicationHelper s end + def get_user_identity identity + s = "" + case identity + when 0 + s = '教师' + when 1 + s = '学生' + when 2 + s = '组织' + when 3 + s= '开发者' + else + s = '学生' + end + s + end + def get_memo @new_memo = Memo.new @public_forum = Forum.find(1) rescue ActiveRecord::RecordNotFound diff --git a/app/helpers/student_work_helper.rb b/app/helpers/student_work_helper.rb index e1cf3464e..df5c872f8 100644 --- a/app/helpers/student_work_helper.rb +++ b/app/helpers/student_work_helper.rb @@ -126,4 +126,15 @@ module StudentWorkHelper end type end + + def revise_attachment_status homework, attach + date = Time.parse(format_time(attach.created_on.to_s)).strftime("%Y-%m-%d") + status = "" + if homework.homework_detail_manual && ((homework.anonymous_comment == 0 &&homework.homework_detail_manual.evaluation_start.to_s <= date) || (homework.anonymous_comment == 1 && homework.end_time < date)) + status = "此时其他同学作品已公开" + else + status = "此时其他同学作品尚未公开" + end + return status + end end \ No newline at end of file diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 9c6bb9cb1..999cefdaa 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -17,7 +17,7 @@ require "digest/md5" require "fileutils" - +require 'elasticsearch/model' class Attachment < ActiveRecord::Base belongs_to :container, :polymorphic => true belongs_to :project, foreign_key: 'container_id', conditions: "attachments.container_type = 'Project'" @@ -38,6 +38,18 @@ class Attachment < ActiveRecord::Base validates :description, length: {maximum: 254} validate :validate_max_file_size + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 5 } do + mappings dynamic: 'false' do + indexes :filename, analyzer: 'smartcn',index_options: 'offsets' + indexes :downloads, analyzer: 'smartcn',index_options: 'offsets' + end + end + acts_as_taggable acts_as_event :title => :filename, @@ -74,9 +86,9 @@ class Attachment < ActiveRecord::Base @@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails") before_save :files_to_final_location,:act_as_course_activity - after_create :office_conver, :be_user_score,:act_as_forge_activity - after_update :office_conver, :be_user_score - after_destroy :delete_from_disk,:down_user_score + after_create :office_conver, :be_user_score,:act_as_forge_activity,:create_attachment_ealasticsearch_index + after_update :office_conver, :be_user_score,:update_attachment_ealasticsearch_index + after_destroy :delete_from_disk,:down_user_score,:delete_attachment_ealasticsearch_index # add by nwb # 获取所有可公开的资源文件列表 @@ -92,7 +104,35 @@ class Attachment < ActiveRecord::Base " LEFT JOIN #{News.table_name} ON #{Attachment.table_name}.container_type='News' AND (#{News.table_name}.project_id in "+self.public_project_id + " OR #{News.table_name}.course_id in " + self.public_course_id + ")" + " LEFT JOIN #{HomeworkAttach.table_name} ON #{Attachment.table_name}.container_type='HomeworkAttach' AND #{HomeworkAttach.table_name}.bid_id in "+self.public_bid_id) } - + scope :indexable,lambda { where('is_public = 1 and ((container_type in ("Principal")) ' + + 'or (container_type = "Course" and container_id in( SELECT `courses`.id FROM `courses` WHERE (courses.status <> 9 AND courses.is_public = 1)) )'+ + 'or (container_type = "Project" and container_id in(SELECT `projects`.id FROM `projects` WHERE (projects.status <> 9 AND projects.is_public = 1) ))' +')')} #用于elastic建索引的scope + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + type:"most_fields", + operator: "or", + fields: ['filename'] + } + }, + sort:{ + _score:{order:"desc"}, + downloads: {order:"desc"} + + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + filename: {} + } + } + } + ) + end # add by nwb # 公开的项目id列表 def self.public_project_id @@ -561,4 +601,46 @@ class Attachment < ActiveRecord::Base end end + def create_attachment_ealasticsearch_index + if self.is_public == 1 && ( (self.container_type == 'Project' && Project.find(self.container_id).is_public == 1) || + ( self.container_type == 'Course' && Course.find(self.container_id).is_public == 1) || + self.container_type == 'Principal') + self.__elasticsearch__.index_document + end + end + def update_attachment_ealasticsearch_index + if self.is_public == 1 && ( (self.container_type == 'Project' && Project.find(self.container_id).is_public == 1) || + ( self.container_type == 'Course' && Course.find(self.container_id).is_public == 1) || + self.container_type == 'Principal') + begin + self.__elasticsearch__.update_document + rescue => e + end + else + begin + self.__elasticsearch__.delete_document + rescue => e + end + end + end + def delete_attachment_ealasticsearch_index + begin + self.__elasticsearch__.delete_document + rescue => e + end + end end + +# Delete the previous articles index in Elasticsearch +# Attachment.__elasticsearch__.client.indices.delete index: Attachment.index_name rescue nil +# +# # Create the new index with the new mapping +# Attachment.__elasticsearch__.client.indices.create \ +# index: Attachment.index_name, +# body: { settings: Attachment.settings.to_hash, mappings: Attachment.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +#暂时只做公开课程/项目里的公开资源 和其他的公开资源 +#Attachment.where('is_public = 1 and ((container_type in ("Principal")) ' + +# 'or (container_type = "Course" and container_id in( SELECT `courses`.id FROM `courses` WHERE (courses.status <> 9 AND courses.is_public = 1)) )'+ +# 'or (container_type = "Project" and container_id in(SELECT `projects`.id FROM `projects` WHERE (projects.status <> 9 AND projects.is_public = 1) ))' +')').import :force=>true diff --git a/app/models/board.rb b/app/models/board.rb index edcbe0c9d..67d59e599 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -17,8 +17,8 @@ class Board < ActiveRecord::Base include Redmine::SafeAttributes - belongs_to :project - belongs_to :course + belongs_to :project,:touch => true + belongs_to :course,:touch=>true has_many :topics, :class_name => 'Message', :conditions => "#{Message.table_name}.parent_id IS NULL", :order => "#{Message.table_name}.created_on DESC" has_many :messages, :dependent => :destroy, :order => "#{Message.table_name}.created_on DESC" belongs_to :last_message, :class_name => 'Message', :foreign_key => :last_message_id diff --git a/app/models/comment.rb b/app/models/comment.rb index 9de25c50d..69753d81c 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -31,7 +31,7 @@ class Comment < ActiveRecord::Base :title=>Proc.new {|o| "RE: #{o.commented.title}" }, :url => Proc.new {|o| {:controller => 'news', :action => 'show', :id => o.commented.id} } - belongs_to :commented, :polymorphic => true, :counter_cache => true + belongs_to :commented, :polymorphic => true, :counter_cache => true,:touch => true belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' validates_presence_of :commented, :author, :comments safe_attributes 'comments' @@ -81,8 +81,10 @@ class Comment < ActiveRecord::Base # 课程成员得分(英雄榜) def act_as_student_score - unless self.author.allowed_to?(:as_teacher, self.commented.course) - course_member_score(self.commented.course.id, self.author_id, "NewReply") + if self.commented.course + unless self.author.allowed_to?(:as_teacher, self.commented.course) + course_member_score(self.commented.course.id, self.author_id, "NewReply") + end end end diff --git a/app/models/course.rb b/app/models/course.rb index 143358abe..1c1bedb3b 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -1,10 +1,24 @@ - +require 'elasticsearch/model' class Course < ActiveRecord::Base include Redmine::SafeAttributes STATUS_ACTIVE = 1 STATUS_CLOSED = 5 STATUS_ARCHIVED = 9 + + #elasticsearch + include Elasticsearch::Model + + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 5 } do + mappings dynamic: 'false' do + indexes :name, analyzer: 'smartcn',index_options: 'offsets' + indexes :description, analyzer: 'smartcn',index_options: 'offsets' + indexes :updated_at, analyzer: 'smartcn',index_options: 'offsets',type:"date" + end + end attr_accessible :code, :extra, :name, :state, :tea_id, :time , :location, :state, :term, :password,:is_public,:description,:class_period, :open_student, :enterprise_name #belongs_to :project, :class_name => 'Course', :foreign_key => :extra, primary_key: :identifier @@ -56,9 +70,9 @@ class Course < ActiveRecord::Base validates_length_of :description, :maximum => 10000 before_save :self_validate # 公开课程变成私有课程,所有资源都变成私有 - after_update :update_files_public - after_create :create_board_sync, :act_as_course_activity, :act_as_course_message - before_destroy :delete_all_members + after_update :update_files_public,:update_course_ealasticsearch_index + after_create :create_board_sync, :act_as_course_activity, :act_as_course_message,:create_course_ealasticsearch_index + before_destroy :delete_all_members,:delete_course_ealasticsearch_index safe_attributes 'extra', 'time', @@ -100,6 +114,34 @@ class Course < ActiveRecord::Base where(" LOWER(name) LIKE :p ", :p => pattern) end } + scope :indexable,lambda { where('is_public = 1') } + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + type:"most_fields", + operator: "or", + fields: ['name', 'description^0.5'] + } + }, + sort: { + _score:{order: "desc" }, + updated_at:{order:"desc"} + + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + name: {}, + description: {} + } + } + } + ) + end def visible?(user=User.current) user.allowed_to?(:view_course, self) @@ -344,6 +386,57 @@ class Course < ActiveRecord::Base #def name # read_attribute('name') || Project.find_by_identifier(self.extra).try(:name) #end + + # after_commit on: [:create] do + # __elasticsearch__.index_document + # end + # + # after_commit on: [:update] do + # __elasticsearch__.update_document + # end + # + # after_commit on: [:destroy] do + # __elasticsearch__.delete_document + # end + def create_course_ealasticsearch_index + if self.is_public == 1 + self.__elasticsearch__.index_document + end + end + def update_course_ealasticsearch_index + if self.is_public == 1 #如果是初次更新成为公开的情况,会报错,那么这条记录尚未被索引过。没有报错就是更新的其他属性 + begin + self.__elasticsearch__.update_document + rescue => e + self.__elasticsearch__.index_document + end + else #如果是更新成为私有的,那么索引就要被删除 + begin + self.__elasticsearch__.delete_document + rescue => e + + end + end + end + + def delete_course_ealasticsearch_index + begin + self.__elasticsearch__.delete_document + rescue => e + + end + end end +# Delete the previous articles index in Elasticsearch +# Course.__elasticsearch__.client.indices.delete index: Course.index_name rescue nil +# +# # Create the new index with the new mapping +# Course.__elasticsearch__.client.indices.create \ +# index: Course.index_name, +# body: { settings: Course.settings.to_hash, mappings: Course.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +#Course.where('is_public = 1').import :force=>true + diff --git a/app/models/document.rb b/app/models/document.rb index 37983d6d4..b45a74775 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -17,7 +17,7 @@ class Document < ActiveRecord::Base include Redmine::SafeAttributes - belongs_to :project + belongs_to :project,:touch=>true belongs_to :user belongs_to :category, :class_name => "DocumentCategory", :foreign_key => "category_id" include UserScoreHelper diff --git a/app/models/exercise.rb b/app/models/exercise.rb index e4295971e..c91e59fd9 100644 --- a/app/models/exercise.rb +++ b/app/models/exercise.rb @@ -2,7 +2,22 @@ class Exercise < ActiveRecord::Base #exercise_status: 1,新建;2,发布;3,关闭 include Redmine::SafeAttributes belongs_to :user + belongs_to :course ,:touch => true has_many :exercise_questions, :dependent => :destroy,:order => "#{ExerciseQuestion.table_name}.question_number" has_many :exercise_users, :dependent => :destroy has_many :users, :through => :exercise_users #该测试被哪些用户提交答案过 + # 课程消息 + has_many :course_messages, :class_name =>'CourseMessage', :as => :course_message, :dependent => :destroy + after_create :acts_as_course_message + def acts_as_course_message + if self.course + if self.exercise_status == 2 #未发布 + #self.course.members.each do |m| + self.course_messages << CourseMessage.create(:user_id => User.current.id, :course_id => self.course_id, :viewed => false,:status=>2) + #end + # else + # self.course_messages.destroy_all 这里的destory_all值得商榷。因为我这里是通过status来控制不同的status的 + end + end + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 2a6da44c5..edce3310a 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -19,7 +19,7 @@ class Issue < ActiveRecord::Base include Redmine::SafeAttributes include Redmine::Utils::DateCalculation include UserScoreHelper - belongs_to :project + belongs_to :project,:touch=> true belongs_to :tracker belongs_to :status, :class_name => 'IssueStatus', :foreign_key => 'status_id' belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' diff --git a/app/models/journals_for_message.rb b/app/models/journals_for_message.rb index eaece95ce..ec6390408 100644 --- a/app/models/journals_for_message.rb +++ b/app/models/journals_for_message.rb @@ -21,9 +21,10 @@ class JournalsForMessage < ActiveRecord::Base after_destroy :delete_kindeditor_assets belongs_to :project, :foreign_key => 'jour_id', - :conditions => "#{self.table_name}.jour_type = 'Project' " + :conditions => "#{self.table_name}.jour_type = 'Project' ",:touch => true + belongs_to :course, - :foreign_key => 'jour_id' + :foreign_key => 'jour_id',:touch=>true belongs_to :jour, :polymorphic => true diff --git a/app/models/message.rb b/app/models/message.rb index 21e5e1c71..92ec0235e 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -20,7 +20,7 @@ class Message < ActiveRecord::Base include UserScoreHelper include ApplicationHelper has_many_kindeditor_assets :assets, :dependent => :destroy - belongs_to :board + belongs_to :board,:touch => true belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' has_many :praise_tread, as: :praise_tread_object, dependent: :destroy @@ -287,13 +287,15 @@ class Message < ActiveRecord::Base # 课程成员得分(英雄榜) def act_as_student_score - unless self.author.allowed_to?(:as_teacher, self.course) - if self.parent_id.nil? - # 发帖 - course_member_score(self.course.id, self.author_id, "Message") - else - # 回帖 - course_member_score(self.course.id, self.author_id, "MessageReply") + if self.course + unless self.author.allowed_to?(:as_teacher, self.course) + if self.parent_id.nil? + # 发帖 + course_member_score(self.course.id, self.author_id, "Message") + else + # 回帖 + course_member_score(self.course.id, self.author_id, "MessageReply") + end end end end diff --git a/app/models/news.rb b/app/models/news.rb index e9b8b5314..53581a536 100644 --- a/app/models/news.rb +++ b/app/models/news.rb @@ -17,11 +17,11 @@ class News < ActiveRecord::Base include Redmine::SafeAttributes - belongs_to :project + belongs_to :project,:touch => true include ApplicationHelper has_many_kindeditor_assets :assets, :dependent => :destroy #added by nwb - belongs_to :course + belongs_to :course,:touch => true belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' has_many :comments, :as => :commented, :dependent => :destroy, :order => "created_on" # fq diff --git a/app/models/project.rb b/app/models/project.rb index 0b0420920..ff546e098 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -14,7 +14,7 @@ # 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 'elasticsearch/model' class Project < ActiveRecord::Base include Redmine::SafeAttributes ProjectType_project = 0 @@ -30,6 +30,19 @@ class Project < ActiveRecord::Base # Specific overidden Activities + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 5 } do + mappings dynamic: 'false' do + indexes :name, analyzer: 'smartcn',index_options: 'offsets' + indexes :description, analyzer: 'smartcn',index_options: 'offsets' + indexes :updated_on, analyzer: 'smartcn',index_options: 'offsets', type:'date' + end + end + has_many :student_works 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}" @@ -138,8 +151,9 @@ class Project < ActiveRecord::Base #ActiveModel::Dirty 这里有一个changed方法。对任何对象都可以用 after_save :update_inherited_members, :if => Proc.new {|project| project.inherit_members_changed?} # 创建project之后默认创建一个board,之后的board去掉了board的概念 - after_create :create_board_sync,:acts_as_forge_activities - before_destroy :delete_all_members + after_create :create_board_sync,:acts_as_forge_activities,:create_project_ealasticsearch_index + before_destroy :delete_all_members,:delete_project_ealasticsearch_index + after_update :update_project_ealasticsearch_index def remove_references_before_destroy return if self.id.nil? Watcher.delete_all ['watchable_id = ?', id] @@ -172,7 +186,33 @@ class Project < ActiveRecord::Base } scope :project_entities, -> { where(project_type: ProjectType_project) } scope :course_entities, -> { where(project_type: ProjectType_course) } - + scope :indexable,lambda { where('is_public = 1')} #用于elastic建索引的scope + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + type:"most_fields", + operator: "or", + fields: ['name','description^0.5'] + } + }, + sort: { + _score:{order: "desc" }, + updated_on:{order: "desc" } + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + name: {}, + description: {} + } + } + } + ) + end def new_course self.where('project_type = ?', 1) end @@ -1176,5 +1216,36 @@ class Project < ActiveRecord::Base end + def create_project_ealasticsearch_index + if self.is_public + self.__elasticsearch__.index_document + end + end + def update_project_ealasticsearch_index + if self.is_public #如果是初次更新成为公开的情况,会报错,那么这条记录尚未被索引过。没有报错就是更新的其他属性 + begin + self.__elasticsearch__.update_document + rescue => e + self.__elasticsearch__.index_document + end + else #如果是更新成为私有的,那么索引就要被删除 + begin + self.__elasticsearch__.delete_document + rescue => e + + end + end + end + def delete_project_ealasticsearch_index + begin + self.__elasticsearch__.delete_document + rescue => e + + end + end + + end +#Project.where('is_public = 1').import :force=>true + diff --git a/app/models/project_tags.rb b/app/models/project_tags.rb index 16de1ea45..bcf666fb5 100644 --- a/app/models/project_tags.rb +++ b/app/models/project_tags.rb @@ -2,7 +2,7 @@ class ProjectTags < ActiveRecord::Base attr_accessible :description, :project_id, :tag_id, :user_id ####################################################################################################添加代码 - belongs_to :project + belongs_to :project,:touch => true belongs_to :tag belongs_to :user diff --git a/app/models/student_work.rb b/app/models/student_work.rb index 010ede635..884f4a089 100644 --- a/app/models/student_work.rb +++ b/app/models/student_work.rb @@ -10,6 +10,7 @@ class StudentWork < ActiveRecord::Base has_many :student_work_tests, order: 'id desc' # course's message has_many :course_messages, :class_name =>'CourseMessage', :as => :course_message, :dependent => :destroy + has_many :attachments, :dependent => :destroy before_destroy :delete_praise before_save :set_program_score, :set_src diff --git a/app/models/user.rb b/app/models/user.rb index cd8b96d47..9c437a186 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require "digest/sha1" - +require 'elasticsearch/model' class User < Principal TEACHER = 0 STUDENT = 1 @@ -25,6 +25,20 @@ class User < Principal include Redmine::SafeAttributes seems_rateable_rater + #elasticsearch + include Elasticsearch::Model + #elasticsearch kaminari init + Kaminari::Hooks.init + Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari + settings index: { number_of_shards: 5 } do + mappings dynamic: 'false' do + indexes :login, analyzer: 'smartcn',index_options: 'offsets' + indexes :firstname, analyzer: 'smartcn',index_options: 'offsets' + indexes :lastname, analyzer: 'smartcn',index_options: 'offsets' + indexes :last_login_on, analyzer: 'smartcn',index_options: 'offsets',type: 'date' + end + end + # Different ways of displaying/sorting users USER_FORMATS = { :firstname_lastname => { @@ -64,6 +78,7 @@ class User < Principal }, } + #每日一报、一事一报、不报 MAIL_NOTIFICATION_OPTIONS = [ #['week', :label_user_mail_option_week], @@ -162,6 +177,7 @@ class User < Principal ##### has_many :shares ,:dependent => :destroy + # add by zjc has_one :level, :class_name => 'UserLevels', :dependent => :destroy has_many :memos , :foreign_key => 'author_id' @@ -222,12 +238,12 @@ class User < Principal # validates_email_realness_of :mail before_create :set_mail_notification before_save :update_hashed_password - before_destroy :remove_references_before_destroy + before_destroy :remove_references_before_destroy,:delete_user_ealasticsearch_index # added by fq - after_create :act_as_activity, :add_onclick_time, :act_as_principal_activity + after_create :act_as_activity, :add_onclick_time, :act_as_principal_activity,:create_user_ealasticsearch_index # end # 更新邮箱用户或用户名的同事,同步更新邀请信息 - after_update :update_invite_list + after_update :update_invite_list,:update_user_ealasticsearch_index include Trustie::Gitlab::ManageUser @@ -240,7 +256,7 @@ class User < Principal where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) } scope :sorted, lambda { order(*User.fields_for_order_statement)} - + scope :indexable,lambda { where('id not in (2,4)')} #用于elastic建索引的scope,id为2是匿名用户,4是管理员,不能被索引 scope :like, lambda {|arg, type| if arg.blank? where(nil) @@ -258,7 +274,33 @@ class User < Principal end end } - + def self.search(query) + __elasticsearch__.search( + { + query: { + multi_match: { + query: query, + type:"most_fields", + operator: "or", + fields: ['login', 'firstname','lastname'] + } + }, + sort:{ + _score:{order:"desc"}, + last_login_on: {order:"desc"} + }, + highlight: { + pre_tags: [''], + post_tags: [''], + fields: { + login: {}, + firstname: {}, + lastname: {} + } + } + } + ) + end # ====================================================================== @@ -435,7 +477,7 @@ class User < Principal end if user # user is already in local database - #return nil unless user.active? + return nil if user.locked? return nil unless user.check_password?(password) else # user is not yet registered, try to authenticate with available sources @@ -1119,6 +1161,23 @@ class User < Principal end end + + def create_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.index_document + end + end + def update_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.update_document + end + end + def delete_user_ealasticsearch_index + if self.id != 2 && self.id != 4 + self.__elasticsearch__.delete_document + end + end + end class AnonymousUser < User @@ -1153,4 +1212,17 @@ class AnonymousUser < User def destroy false end + end + +# Delete the previous articles index in Elasticsearch +# User.__elasticsearch__.client.indices.delete index: User.index_name rescue nil +# +# # Create the new index with the new mapping +# User.__elasticsearch__.client.indices.create \ +# index: User.index_name, +# body: { settings: User.settings.to_hash, mappings: User.mappings.to_hash } + +# Index all article records from the DB to Elasticsearch +# 匿名用户 角色 和 管理员角色不能被索引 +#User.where('id not in (2,4)').import :force=>true diff --git a/app/views/attachments/destroy.js.erb b/app/views/attachments/destroy.js.erb index d23422e24..cfbe68085 100644 --- a/app/views/attachments/destroy.js.erb +++ b/app/views/attachments/destroy.js.erb @@ -1,5 +1,9 @@ <% if @is_destroy%> $("#attachment_<%= @attachment.id%>").remove(); + if(document.getElementById("uploadReviseBox")) { + $("#uploadReviseBox").removeClass('disable_link'); + $("#choose_revise_attach").attr("onclick","_file.click();"); + } <%else%> var attachment_html_obj = $('#attachments_<%= j params[:attachment_id] %>'); //modify by yutao 2015-5-14 当1个页面存在多个上传控件时此块代码存在bug 故改之 start @@ -26,5 +30,10 @@ $('#upload_file_count'+containerid).html(""+count+""+"个文件"+"已上传"); } } + + if(document.getElementById("uploadReviseBox")) { + $("#uploadReviseBox").removeClass('disable_link'); + $("#choose_revise_attach").attr("onclick","_file.click();"); + } //modify by yutao 2015-5-14 当1个页面存在多个上传控件时此块代码存在bug 故改之 end <% end%> \ No newline at end of file diff --git a/app/views/courses/member.html.erb b/app/views/courses/member.html.erb index ce336373b..3aa7d4b57 100644 --- a/app/views/courses/member.html.erb +++ b/app/views/courses/member.html.erb @@ -1,9 +1,14 @@ -