# encoding: utf-8 # REDO: 创建版本库权限控制 class ShixunsController < ApplicationController layout 'base_shixun' # 如要添加或修改before_filter时,请将handle_openi_request这个before_filter放至第一位 # before_filter :handle_openi_request, if: -> { user_login_and_from_openi } before_filter :require_login, :except => [:ghook, :download_file, :show, :index] before_filter :check_authentication, :except => [:ghook, :download_file, :show, :index] before_filter :find_shixun, :except => [ :index, :new, :create, :index, :search, :shixun_courses, :new_disscuss, :shixun_migrate, :qrcode, :download_file, :departments, :get_mirror_script, :send_message_to_administrator] skip_before_filter :verify_authenticity_token, :only => [:ghook, :download_file] before_filter :view_allow, :only => [:collaborators, :propaedeutics, :shixun_discuss, :ranking_list] before_filter :require_manager, :only => [ :settings, :add_script, :publish, :collaborators_delete, :shixun_members_added, :add_collaborators, :update, :destroy] before_filter :validation_email, :only => [:new] before_filter :require_admin, :only => [:destroy] # 移动云ToC模式权限控制 # before_filter :ecloud_auth, :except => [:show, :index] include ApplicationHelper include ShixunsHelper CODES = %W(2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z) DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z) # 实训二维码生成器(android编译成功后,game的picture_path会返回中间层的workspace, workspace+关卡学院文件路径就是文件所在的路径) def qrcode @type == "qrcode" game = Game.find(params[:game_id]) workspace = game.picture_path game_challenge = game.challenge qr = RQRCode::QRCode.new("#{Setting.host_name}/shixuns/download_file?file_name=#{workspace}/#{game_challenge.picture_path}/manual-ok.apk", :size => 10, :level => :h) @qrcode_str = Base64.encode64( qr.to_img.resize(400,400).to_s ) respond_to do |format| format.js end end # 二维码扫描下载 def download_file file_path = params[:file_name] send_file "#{Rails.root}/#{file_path}", :filename => "#{file_path}", :type => 'shixun', :disposition => 'attachment' #inline can open in browser end # 获取版本库文件目录 def search_file_list path = params[:path] @path = to_path_param(path) g = Gitlab.client @dir = g.trees(@shixun.gpid, :path => @path).map{|tree|[tree.type, (@path.blank? ? tree.name : "#{@path}"+"/"+tree.name )]} logger.info("dir is ##{@dir}") respond_to do |format| format.js end # render :json => {:dir => @dir} end def achieve_ways @repository = @shixun.repository @entries = @repository.entries(@path, @rev) # @entries = Gitlab.client.trees(@shixun.gpid).map{|tree| tree.name} end def statistics_students pub_shixun = ApplyAction.where(:container_type => "ApplyShixun", :status => 1, :container_id => @shixun.id).last if pub_shixun.present? updated_at = pub_shixun.updated_at admin_ids = User.where(:admin => 1).blank? ? "(-1)" : "(" + User.where(:admin => 1).map(&:id).join(",") + ")" @statistics = @shixun.myshixuns.where("user_id not in #{admin_ids}").select{|myshixun| myshixun.updated_at > updated_at} @statistic_ids = @statistics.map(&:id) @statistics = Myshixun.where(:id => @statistic_ids).order("created_at desc") #@statistics = @shixun.myshixuns.where("created_at > #{updated_at}").order("created_at desc") @complete_myshixun = Myshixun.where(:id => @statistics, :status => 1) @complete_myshixun_count = @complete_myshixun.size @complete_myshixun.each do |myshixun| myshixun[:s_score] = myshixun.total_score myshixun[:s_spend_time] = myshixun.total_spend_time myshixun[:s_accuracy] = myshixun.total_accuracy myshixun[:s_done_time] = myshixun.done_time end @complete_myshixun = @complete_myshixun.sort do |a, b| [b[:s_score], a[:s_spend_time], b[:s_accuracy], a[:s_done_time]] <=> [a[:s_score], b[:s_spend_time], a[:s_accuracy], b[:s_done_time]] end @complete_myshixun = @complete_myshixun[0, 10] @statistics_count = @statistics.size @statistics = @statistics.limit(10) end end # 学员统计中的关卡统计 def challenge_statistics @challenge = @shixun.challenges.where(:id => params[:c]).first if @challenge pub_shixun = ApplyAction.where(:container_type => "ApplyShixun", :status => 1, :container_id => @shixun.id).last if pub_shixun.present? updated_at = pub_shixun.updated_at admin_ids = User.where(:admin => 1).blank? ? "(-1)" : "(" + User.where(:admin => 1).map(&:id).join(",") + ")" myshixuns = @shixun.myshixuns.where("user_id not in #{admin_ids}").select{|myshixun| myshixun.updated_at > updated_at} @statistics = @challenge.games.where(:myshixun_id => myshixuns.map(&:id), :status => 2).select("games.*, (unix_timestamp(games.end_time)-unix_timestamp(games.open_time)) as spend_time").reorder("final_score desc, spend_time asc, accuracy desc, end_time asc") @statistics_count = @statistics.size @statistics = paginateHelper @statistics, 20 @page = params['page'] || 1 end end end # 学员统计中的通关排行榜 def ranking_list pub_shixun = ApplyAction.where(:container_type => "ApplyShixun", :status => 1, :container_id => @shixun.id).last if pub_shixun.present? updated_at = pub_shixun.updated_at admin_ids = User.where(:admin => 1).blank? ? "(-1)" : "(" + User.where(:admin => 1).map(&:id).join(",") + ")" @complete_myshixun = @shixun.myshixuns.includes(:user, :games => [:challenge]).where("user_id not in #{admin_ids} and status = 1").select{|myshixun| myshixun.updated_at > updated_at} #@complete_myshixun = Myshixun.where(:id => statistics.map(&:id), :status => 1) @complete_myshixun_count = @complete_myshixun.size @complete_myshixun.each do |myshixun| myshixun[:s_score] = myshixun_exp myshixun myshixun[:s_spend_time] = myshixun_spend_time myshixun # myshixun[:s_accuracy] = myshixun_accuracy myshixun # myshixun[:s_done_time] = myshixun_done_time myshixun end # @complete_myshixun = @complete_myshixun.sort do |a, b| # [b[:s_score], a[:s_spend_time], b[:s_accuracy], a[:s_done_time]] <=> [a[:s_score], b[:s_spend_time], a[:s_accuracy], b[:s_done_time]] # end @complete_myshixun = @complete_myshixun.sort do |a, b| [b[:s_score], a[:s_spend_time]] <=> [a[:s_score], b[:s_spend_time]] end @complete_myshixun = @complete_myshixun[0..9] end end # 学员统计中的学员列表 def trainee_list pub_shixun = ApplyAction.where(:container_type => "ApplyShixun", :status => 1, :container_id => @shixun.id).last if pub_shixun.present? updated_at = pub_shixun.updated_at admin_ids = User.where(:admin => 1).blank? ? "(-1)" : "(" + User.where(:admin => 1).map(&:id).join(",") + ")" @statistics = @shixun.myshixuns.where("user_id not in #{admin_ids}").select{|myshixun| myshixun.updated_at > updated_at} @statistics = @statistics.map(&:id) @statistics = Myshixun.where(:id => @statistics).order("created_at desc") @statistics_count = @statistics.size @page = params['page'] || 1 @statistics = paginateHelper @statistics, 20 end end # push代码的时候会触发gitlab hook def ghook # shixun_modify_status_without_publish(@shixun, 1) render :json => {status: "success"} end # 注意:shixun_id 格式 /shixun_migrate?shixun_id=100 # 注意:language 格式 /shixun_migrate?language="Java,Python" def shixun_migrate unless User.current.admin? render_403 return end shixun_tomcat = Redmine::Configuration['shixun_tomcat'] if params[:language] language = params[:language].split("/") logger.info(language) shixuns = Shixun.where(:language => language) elsif params[:shixun_id] shixuns = Shixun.where(:id => params[:shixun_id]) else shixuns = Shixun.all end if shixuns.present? shixuns.each do |shixun| begin logger.info("shixun id is #{shixun.id}") tpiList =[] gameInfo = shixun.gameInfo myshixuns = shixun.myshixuns if myshixuns.present? myshixuns.each do |myshixun| logger.info("tpiID is #{myshixun.id}") tpiID = myshixun.id instanceGitURL = gitlab_url myshixun logger.info("instanceGitURL is #{instanceGitURL}") tpiList << {:tpiID => tpiID, :instanceGitURL => instanceGitURL} logger.info("###############{tpiList.to_json unless tpiList.blank?}") logger.info("************#{tpiList}") end end tpiList = Base64.urlsafe_encode64(tpiList.to_json) unless tpiList.blank? params = {:gameInfo => "#{gameInfo}", :tpiList => "#{tpiList}" } logger.info("params is #{params}") uri = "#{shixun_tomcat}/bridge/dataTransfer/transfer" logger.info("uri is #{uri}") res = uri_exec uri, params logger.info("=====res is #{res['code']}") render :json => {:result => "success"} rescue Exception => e logger.info("error ====> #{e.message}") end end end end # 向管理员发私信 def send_message_to_administrator # notes = User.current.show_name.to_s + " 申请了新镜像:#{params[:notes]}" # JournalsForMessage.create( # :jour_id => 1, # :jour_type => "Principal", # :user_id => User.current.id, # :reply_id => 0, # :is_readed => 0, # :private => 1, # :notes => notes # ) Tiding.create(:user_id => 1, :trigger_user_id => User.current.id, :container_type => "SendMessage", :extra => params[:notes], :viewed => 0, :tiding_type => "Apply") render :json => {success: "true"} end def shixun_test jenkins_shixuns = Redmine::Configuration['jenkins_shixuns'] uri = URI("#{jenkins_shixuns}/jenkins-exec/webssh/getConnectInfo") user_id = User.current.id params = {userID:user_id} res = uri_exec uri, params render :json => {data:"succesed"} end def delete_shixun_test jenkins_shixuns = Redmine::Configuration['jenkins_shixuns'] uri = URI("#{jenkins_shixuns}/jenkins-exec/webssh/deleteSSH") user_id = User.current.id params = {userID:user_id} res = uri_exec uri, params render :json => {data:"succesed"} end def game_webssh end def entry_edit g = Gitlab.client @path = params[:path] @rev = params[:rev] file_content = g.files(@shixun.gpid, @path, @rev).content @content = tran_base64_decode64(file_content) respond_to do |format| format.js end end def entry_update g = Gitlab.client @path = params[:path] @rev = params[:rev] @content = params[:content] code_file = g.edit_file(@shixun.gpid, User.current.login, :content => params[:content], :file_path => @path, :branch_name => @rev, :commit_message => "file update") # if @shixun.try(:status).to_i < 2 # shixun_modify_status_without_publish(@shixun, 1) # end @shixun.myshixuns.update_all(:system_tip => 0) if code_file.message @error_message = code_file.message end respond_to do |format| format.js end end def shixun_monitor # monitor_filter # if @had_exec # @tpm = Myshixun.where(:user_id => User.current, :shixun_id => @shixun).first # end # # respond_to do |format| # format.js # end end def statistics @challenges = @shixun.challenges @top_students = @shixun.myshixuns.where(:status => 1) myshixuns = @shixun.myshixuns.includes(:games) @latest_myshixuns = myshixuns.order("created_at desc").limit(4) end def autocompletion end def edit_md #render :layout => false end # 首页实训导航点击时,type参数 def index @type = params[:type] @status = [["全部状态", 0], ["已发布", 2], ["未发布", 1], ["已关闭", 3]] @diff = ["全部难度", "初级学员", "中级学员", "高级学员", "顶级学员"] @repertoires = Repertoire.includes(sub_repertoires: [:tag_repertoires]).order("updated_at asc") if @type.present? id = params[:id].to_i case @type when 'rep' rep = Repertoire.find id sub_id = rep.sub_repertoires.map(&:id) tag = TagRepertoire.where(:sub_repertoire_id => sub_id) @rep_active = rep.id # 页面样式、和首页跳转搜索需要 @search_name = "" when 'sub' sub = SubRepertoire.find id tag = TagRepertoire.where(:sub_repertoire_id => sub) @rep_active = sub.repertoire.id @sub_acive = sub.id # 页面样式、和首页跳转搜索需要 @search_name = "#{sub.name}" when 'tag' tag = TagRepertoire.find id sub = tag.sub_repertoire @rep_active = sub.repertoire.id @sub_active = sub.id @tag_active = tag.id # 页面样式、和首页跳转搜索需要 @search_name = "#{sub.name} / #{tag.name}" end shixun_id = ShixunTagRepertoire.where(:tag_repertoire_id => tag).map(&:shixun_id) @shixuns = Shixun.select([:id, :name, :user_id, :challenges_count, :visits, :status, :myshixuns_count, :trainee, :use_scope, :identifier, :image_text, :averge_star]) .where(:id => shixun_id, :hidden => 0).where("status != -1") .includes(:challenges, :schools, :shixun_members, :users).order("status = 2 desc, publish_time asc") else @shixuns = Shixun.select([:id, :name, :user_id, :challenges_count, :visits, :status, :myshixuns_count, :trainee, :use_scope, :identifier, :image_text, :averge_star]) .where("status != ? and hidden = ?", -1, 0).includes(:challenges, :schools, :shixun_members, :users) .order("status = 2 desc, publish_time asc") end # # 依据tag和语言推荐实训,如果tag不够,则依据语言推荐;语言不够,则取系统的三个 # @recommend_shixuns = Shixun.find_by_sql("select challenge_id from challenge_tags where name like # CONCAT('%',(select name from challenge_tags where challenge_id in (select id from challenges where shixun_id=61)),'%')") @obj_count = @shixuns.count @limit = 16 @is_remote = true @obj_pages = Paginator.new @obj_count, @limit, params['page'] || 1 @offset ||= @obj_pages.offset @shixuns = paginateHelper @shixuns, @limit respond_to do |format| format.js format.html{render :layout => "base_edu"} end end # params # repertoire:大类别; #sub_repertoire:子类别; #tag_repertoire 实训标签; # status:实训状态; diff:实训难度; search: 搜索条件; order:排序; sort: 升序, 降序 def search repertoire = params[:repertoire] sub_repertoire = params[:sub_repertoire] tag_repertoire = params[:tag_repertoire] status = params[:status].to_i diff = params[:diff].to_i search = params[:search].try(:strip) order = params[:order] bsort = params[:sort] hidden_learn = params[:hidden_learn] logger.info("######params: #{params}") diff = (diff == 0 ? [1,2,3,4] : diff) # diff = 0表示搜索所有 filter_status = order == "created_at" || order == "myshixuns_count" ? [2, 3] : [0, 1, 2, 3] status = if status == 0 filter_status & [0, 1, 2 ,3] # 所有状态 elsif status == 1 filter_status & [0, 1] # 未发布 else filter_status & [status] end shixun_id = if tag_repertoire.present? tag = TagRepertoire.find(tag_repertoire) ShixunTagRepertoire.where(:tag_repertoire_id => tag).map(&:shixun_id) elsif sub_repertoire.present? sub = SubRepertoire.find(sub_repertoire) ShixunTagRepertoire.where(:tag_repertoire_id => sub.tag_repertoires).map(&:shixun_id) elsif repertoire.present? rep = Repertoire.find(repertoire) tag_id = TagRepertoire.where(:sub_repertoire_id => rep.sub_repertoires).map(&:id) ShixunTagRepertoire.where(:tag_repertoire_id => tag_id).map(&:shixun_id) else Shixun.select([:id]).map(&:id) end # "我的"实训 if order == "mine" my_shixun_ids = User.current.shixun_members.map(&:shixun_id) + User.current.myshixuns.map(&:shixun_id) shixun_id = shixun_id & my_shixun_ids end @shixuns = Shixun.select([:id, :name, :user_id, :challenges_count, :visits, :status, :myshixuns_count, :trainee, :use_scope, :identifier, :image_text, :averge_star]).where(:id => shixun_id, :hidden => 0, :trainee => diff, :status => status).includes(:challenges, :schools, :shixun_members, :users) if search.present? search_users_id = User.select([:id]).where("concat(lastname, firstname) like '%#{search}%'") search_users_id = search_users_id.blank? ? "(-1)" : "(" + search_users_id.map{|sc| sc.id}.join(',') + ")" search_shixun_ids = Challenge.where("subject like '%#{search}%'").pluck(:shixun_id).uniq search_shixun_ids = search_shixun_ids.blank? ? "(-1)" : "(" + search_shixun_ids.join(",") + ")" fuzzy_searchs = search.split(" ").join("%") @shixuns = @shixuns.where("name like ? or user_id in #{search_users_id} or id in #{search_shixun_ids}", "%#{fuzzy_searchs}%") end if hidden_learn.present? shixun_id = User.current.shixuns.map(&:id) my_shixun_id = Myshixun.where(:user_id => User.current.id).map(&:shixun_id) not_shixun_id = shixun_id + my_shixun_id not_shixun_id = not_shixun_id.size > 0 ? "(" + not_shixun_id.join(",") + ")" : "(-1)" @shixuns = @shixuns.where("id not in #{not_shixun_id}") if shixun_id.present? end if order == "mine" @shixuns = @shixuns.order("created_at #{bsort}") else @shixuns = @shixuns.order("shixuns.status = 2 desc, #{order} #{bsort}") end @obj_count = @shixuns.count @limit = 16 @is_remote = true @obj_pages = Paginator.new @obj_count, @limit, params['page'] || 1 @offset ||= @obj_pages.offset @shixuns = paginateHelper @shixuns, @limit respond_to do |format| format.js end end def propaedeutics respond_to do |format| format.html end end def update_propaedeutics if request.get? respond_to do |format| format.html end else @shixun = Shixun.where(:identifier => params[:id]).first @shixun.update_attribute("propaedeutics", params[:shixun][:propaedeutics]) redirect_to propaedeutics_shixun_path(@shixun) end end def collaborators @collaborators = @shixun.shixun_members.reorder("role = 1 desc, created_at asc") @collaborators_count = @collaborators.size @limit = 10 respond_to do |format| format.js format.html end end def collaborators_delete user_id = params[:c_id] gid = User.find(user_id).try(:gid) shixun_member = ShixunMember.where(:user_id => user_id, :shixun_id => @shixun.id, :role => 2).first shixun_member.delete g = Gitlab.client unless gid.nil? g.remove_team_member(@shixun.gpid, gid) end @collaborators = @shixun.shixun_members.reorder("role = 1 desc, created_at asc") @collaborators_count = @collaborators.count @limit = 10 respond_to do |format| format.js end end # 已存在的成员不添加 def add_collaborators if !params[:search].nil? member_ids = "(" + @shixun.shixun_members.map(&:user_id).join(',') + ")" condition = "%#{params[:search].strip}%".gsub(" ","") @users = User.where("id not in #{member_ids} and status = 1 and LOWER(concat(lastname, firstname, login, mail, nickname)) LIKE '#{condition}'").includes(:user_extensions) end respond_to do |format| format.js end end # 事务处理,保证Trustie与Gitlab数据同步 def shixun_members_added ActiveRecord::Base.transaction do begin unless params[:membership][:user_ids].blank? memberships = params[:membership][:user_ids] memberships.each do |member| user = User.find(member) s = Trustie::Gitlab::Sync.new if user.gid.present? gid = user.gid else guser = s.sync_user(user) gid = guser.id end ShixunMember.create!(:user_id => member, :shixun_id => @shixun.id, :role => 2) u = s.g.add_team_member(@shixun.gpid, gid, 40) # 3代表角色master if u.blank? raise("同步Gitlab数据失败") end end respond_to do |format| format.js{redirect_to collaborators_shixun_path(@shixun)} end end rescue Exception => e logger.error(e) raise ActiveRecord::Rollback end end end def change_manager if request.get? @collaborators = @shixun.shixun_members.where("user_id != #{@shixun.user_id}") else if params[:choosemanager] man_member = ShixunMember.where(:shixun_id => @shixun.id, :user_id => @shixun.user_id).first cha_member = ShixunMember.find params[:choosemanager] if man_member && cha_member man_member.update_attributes(:role => 2) cha_member.update_attributes(:role => 1) @shixun.update_attributes(:user_id => cha_member.user_id) end redirect_to collaborators_shixun_path(@shixun) end end end # gameID 及实训ID # status: 0 , 1 申请过, 2,实训关卡路径未填, 3 实训标签未填, 4 实训未创建关卡 def publish @status = 0 @position = "" begin if @shixun.challenges.count == 0 @status = 4 else @shixun.challenges.each do |challenge| if challenge.challenge_tags.count == 0 @status = 3 @position = (@position == "" ? @position : (@position + ",")) + challenge.position.to_s + "关" end end unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil) if unfinish_challenge.count > 0 && !@shixun.is_choice_type @status = 2 @pos = [] unfinish_challenge.each do |challenge| @pos << challenge.position end @pos = @pos.join(",") end end if @status == 0 @shixun.update_attributes!(:status => 1) apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first if apply && apply.status == 0 @status = 0 else ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => User.current.id, :status => 0) begin status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员') rescue => e Rails.logger.error "发送验证码出错: #{e}" end @status = 1 end end rescue Exception => e logger.error("pushlish game #{e}") respond_to do |format| format.js end end end def apply_publish apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).last if apply && apply.status == 0 @status = 0 else ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => User.current.id, :status => 0) # notes = User.current.show_name.to_s + " 申请发布实训:#{@shixun.name}" # JournalsForMessage.create(:jour_id => 1, :jour_type => 'Principal', :user_id => User.current.id, :notes => notes, :private => 1, :reply_id => 0) @shixun.update_column('status', 1) @status = 1 end end def cancel_publish apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first if apply && apply.status == 0 apply.update_attributes(:status => 3) apply.tidings.destroy_all @shixun.update_column('status', 0) end redirect_to shixun_path(@shixun) end def close render_403 unless User.current.admin? @shixun.update_attributes(:status => 3, :closer_id => User.current.id, :end_time => Time.now) redirect_to shixun_path(@shixun) end # copy_shixun:复制一个新的实训模块包括版本库 # copy_myshixun自动创建系列game,game中只包含状态等信息,公共信息从Challeges中读取 # 开启过实训的则直接跳入my实训页面 def shixun_exec # unless allow_shixun_exec(@shixun) && (User.current.manager_of_shixun?(@shixun) || @shixun.status > 0) # render_403 # return # end # 添加事务锁,防止并发情况下tpi被多次创建 myshixun = Myshixun.where(:user_id => User.current.id, :shixun_id => @shixun.id).first unless myshixun.blank? logger.info("current task id is #{myshixun.current_task}") redirect_to "/tasks/#{myshixun.current_task.identifier}" # redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun, :is_subject => params[:is_subject]) return end ActiveRecord::Base.transaction do begin # fork版本库,如果用户没有同步,则先同步用户 g = Gitlab.client if User.current.gid.nil? s = Trustie::Gitlab::Sync.new s.sync_user(User.current) end gshixun = g.fork(@shixun.gpid, User.current.gid) shixun_tomcat = Redmine::Configuration['shixun_tomcat'] code = down_generate_identifier("myshixun") # 一般通过默认分支是否存在来判断一个项目是否fork成功 if gshixun.try(:id).present? commit_id = g.commits(@shixun.gpid).first.try(:id) # educoder 加入到myshixun中 myshixun_admin_gid = User.where(:login => "educoder").first.try(:gid) g.add_team_member(gshixun.id, myshixun_admin_gid, 40) # 40代表角色master myshixun = Myshixun.create!(:shixun_id => @shixun.id, :user_id => User.current.id, :identifier => code, :modify_time => @shixun.modify_time, :reset_time => @shixun.reset_time, :onclick_time => Time.now, :gpid => gshixun.id, :git_url => gshixun.try(:path_with_namespace), :commit_id => commit_id) # tpm 不需要目录的 # rep = Repository.create!(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2) # rep.update_column(:type, "Repository::Gitlab") rep_url = Base64.urlsafe_encode64(gitlab_url @shixun) # 注意:educoder为默认给实训创建版本库的用户,如果换成别的用户,名字要相应的修改 logger.info("start openGameInstance") uri = "#{shixun_tomcat}/bridge/game/openGameInstance" logger.info("end openGameInstance") params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: gshixun.try(:name)} logger.info("openGameInstance params is #{params}") res = uri_exec uri, params if (res && res['code'].to_i != 0) raise("实训云平台繁忙(繁忙等级:83)") end # 其它创建关卡等操作 challenges = @shixun.challenges # 之所以增加user_id是为了方便统计查询性能 challenges.each_with_index do |challenge, index| status = (index == 0 ? 0 : 3) code = down_generate_identifier("game") Game.create!(:challenge_id => challenge.id, :myshixun_id => myshixun.id, :status => status, :user_id => myshixun.user_id, :open_time => Time.now, :identifier => code, :modify_time => challenge.modify_time) # 记录刚开始时默认代开文件原始代码 # 刚开始fork的一段时间是获取不了content内容的 # if challenge.st == 0 && challenge.path.present? # paths = challenge.path.split(";") # paths.each do |path| # game_code_init(game.id, path) # end # end end if params[:type] == "1" # 重置过来的请求 shixun_mod = ShixunModify.where(:shixun_id => myshixun.try(:shixun_id), :myshixun_id => myshixun.try(:id)).first if shixun_mod.nil? ShixunModify.create(:shixun_id => myshixun.shixun_id, :myshixun_id => myshixun.id, :status => 0) else shixun_mod.update_attributes(:status => 0) end end else raise("实训云平台繁忙(繁忙等级:81)") end # unlock logger.info("myshixun id is #{myshixun.try(:identifier)} and current_task id is#{myshixun.try(:current_task).try(:id)}") # 开启实训时更新关联作品的状态 update_myshixun_work_status myshixun redirect_to myshixun_game_path(myshixun.current_task, :myshixun_id => myshixun, :is_subject => params[:is_subject]) rescue Exception => e if e.message == "shixun error" flash[:error] = "正在后台执行,请稍后重试" elsif e.message.include?("Mysql2::Error") flash[:error] = "正在后台执行,请稍后重试" else flash[:error] = e.message end logger.error("###failed to exec shixun: current task id is #{e}") g.delete_project(gshixun.id) if gshixun.try(:id).present? redirect_to shixun_challenges_path(@shixun) raise ActiveRecord::Rollback end end end def shixun_courses data = {result:0,options:[]} if params[:major_id] != 0 major = Major.find params[:major_id] if major data[:result] = 1 major_courses = major.major_courses CourseList.where(:id => major_courses.map(&:course_list_id)).each do |course| option = [] option << course.name.to_s option << course.id data[:options] << option end else data[:result] = 0 end else data[:result] = 0 end render :json =>data end def new @shixun = Shixun.new @support = Major.where(:support_shixuns=> 1) @introduction_sample = PlatformSample.where(:samples_type => 'introduction').first.try(:contents) @knowledge_sample = PlatformSample.where(:samples_type => 'knowledge').first.try(:contents) @main_type = MirrorRepository.published_main_mirror @small_type = MirrorRepository.published_small_mirror respond_to do |format| format.html{render :layout => 'base_edu'} format.json end end def departments respond_to do |format| format.js end end def create identifier = generate_identifier @shixun = Shixun.new(params[:shixun]) @shixun.user_id = User.current.id @shixun.trainee = params[:trainee] @shixun.webssh = params[:webssh].to_i @shixun.multi_webssh = @shixun.webssh == 2 && params[:multi_webssh] == "1" ? 1 : 0 @shixun.vnc = params[:vnc].to_i @shixun.can_copy = params[:can_copy].to_i @shixun.identifier = identifier @shixun.reset_time = Time.now @shixun.modify_time = Time.now @shixun.use_scope = params[:public_degree].to_i @shixun.visits = 1 main_type = params[:main_type] sub_type = params[:small_type] mirror = MirrorScript.where(:mirror_repository_id => main_type) if sub_type.blank? shixun_script = mirror.first.try(:script) else main_mirror = MirrorRepository.find(main_type).type_name sub_mirror = MirrorRepository.find(sub_type).type_name if main_mirror == "Java" && sub_mirror == "Mysql" shixun_script = mirror.last.try(:script) else shixun_script = mirror.first.try(:script) shixun_script = modify_shixun_script @shixun, shixun_script end end @shixun.evaluate_script = shixun_script ActiveRecord::Base.transaction do begin @shixun.save! if params[:scope_partment].present? arr = [] ids = School.where(:name => params[:scope_partment]).map(&:id).uniq ids.each do |id| arr << { :school_id => id, :shixun_id => @shixun.id } end ShixunSchool.create!(arr) end m = ShixunMember.new(:user_id => User.current.id, :role => 1) @shixun.shixun_members << m # 镜像-实训关联表 ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present? if sub_type.present? sub_mirrors = sub_type.split(",").map(&:to_i) sub_mirrors.each do |mirror| ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) end end # 自动构建版本库 repository = Repository.new repository.shixun = @shixun repository.type = 'Repository::Gitlab' repository.identifier = @shixun.identifier.downcase repository.project_id = -1 repository.save! s = Trustie::Gitlab::Sync.new gproject = s.create_shixun(@shixun, repository) raise "版本库创建失败" if @shixun.gpid.blank? # 若和gitlab没同步成功,则抛出异常 g = Gitlab.client @shixun.update_column(:git_url, g.project(@shixun.gpid).path_with_namespace) # g = Gitlab.client # hook_url = Setting.protocol + "://" + Setting.host_name + "/shixuns/#{@shixun.identifier}" + "/ghook" # g.add_project_hook(@shixun.gpid, hook_url) redirect_to shixun_path @shixun, notice: l(:notice_successful_create) rescue Exception => e respond_to do |format| flash[:notice] = "#{e.message}" redirect_to new_shixun_path raise ActiveRecord::Rollback end end end end def copy # 实训设置了能复制并且(平台认证的老师或者实训管理员二选一) unless (@shixun.can_copy && ((User.current.user_extensions.try(:identity) == 0 && User.current.professional_certification) || User.current.manager_of_shixun?(@shixun))) render_403 return end ActiveRecord::Base.transaction do begin raise "请先绑定邮箱" if User.current.mail.blank? new_shixun = Shixun.new new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "homepage_show","git_url") new_shixun.user_id = User.current.id new_shixun.identifier = generate_identifier new_shixun.status = 0 new_shixun.visits = 1 new_shixun.fork_from = @shixun.id new_shixun.save! shixun_major_courses = ShixunMajorCourse.where(:shixun_id => @shixun.id) if shixun_major_courses.present? shixun_major_courses.each do |smc| ShixunMajorCourse.create!(:shixun_id => new_shixun.id, :major_id => smc.major_id, :course_list_id => smc.course_list_id) end end # 同步镜像 if @shixun.mirror_repositories.present? @shixun.mirror_repositories.each do |mirror| ShixunMirrorRepository.create!(:shixun_id => new_shixun.id, :mirror_repository_id => mirror.id) end end # 同步技术标签 @shixun.shixun_tag_repertoires.each do |str| ShixunTagRepertoire.create!(:tag_repertoire_id => str.tag_repertoire_id, :shixun_id => new_shixun.id) end # 同步复制版本库,先fork再修改版本库名 # eduforge用户作为版本库的创建者 repository = Repository.new repository.shixun = new_shixun repository.type = 'Repository::Gitlab' repository.identifier = new_shixun.identifier.downcase repository.project_id = -1 repository.save! g = Gitlab.client user_gid = User.find_by_mail("eduforge@163.com").try(:gid) gshixun = g.fork(@shixun.gpid, user_gid) raise "版本库创建失败" if gshixun.try(:id).blank? # gshixun = g.edit_project(gshixun.id, new_shixun.identifier, new_shixun.identifier) # raise "实训复制失败" if (gshixun.try(:name) != new_shixun.identifier || gshixun.try(:path) != new_shixun.identifier) new_shixun.update_attributes!(:gpid => gshixun.id, :git_url => gshixun.try(:path_with_namespace)) # 同步复制者至gitlab, 必须要求用户绑定了邮箱 ShixunMember.create!(:user_id => User.current.id, :shixun_id => new_shixun.try(:id), :role => 1) gid = User.current.try(:gid) if gid.nil? s = Trustie::Gitlab::Sync.new gid = s.sync_user(User.current).try(:id) end g.add_team_member(gshixun.id, gid, 40) # 3代表角色master # # 同步复制合作者,合作者加入到gitlab # unless User.current.manager_of_shixun?(@shixun) # ShixunMember.create!(:user_id => User.current.id, :shixun_id => new_shixun.try(:id), :role => 1) # end # @shixun.shixun_members.each do |shixun_member| # if ShixunMember.where(:shixun_id => new_shixun.try(:id), :user_id => shixun_member.user_id).blank? # ShixunMember.create!(:user_id => shixun_member.try(:user_id), :shixun_id => new_shixun.try(:id), # :role => (shixun_member.try(:user_id) == User.current.id ? 1 : 2)) # end # s = Trustie::Gitlab::Sync.new # gid = User.find(shixun_member.user_id).try(:gid) # u = s.g.add_team_member(gshixun.id, gid, 40) # 3代表角色master # if u.blank? # raise("同步Gitlab数据失败") # end # end # 同步复制关卡 if @shixun.challenges.present? @shixun.challenges.each do |challenge| new_challenge = Challenge.new new_challenge.attributes = challenge.attributes.dup.except("id","shixun_id","user_id") new_challenge.user_id = User.current.id new_challenge.shixun_id = new_shixun.id new_challenge.save! if challenge.st == 0 # 评测题 # 同步测试集 if challenge.test_sets.present? challenge.test_sets.each do |test_set| new_test_set = TestSet.new new_test_set.attributes = test_set.attributes.dup.except("id","challenge_id") new_test_set.challenge_id = new_challenge.id new_test_set.save! end end # 同步关卡标签 challenge_tags = ChallengeTag.where("challenge_id =? and challenge_choose_id is null", challenge.id) if challenge_tags.present? challenge_tags.each do |challenge_tag| ChallengeTag.create!(:challenge_id => new_challenge.id, :name => challenge_tag.try(:name)) end end elsif challenge.st == 1 # 选择题 if challenge.challenge_chooses.present? challenge.challenge_chooses.each do |challenge_choose| new_challenge_choose = ChallengeChoose.new new_challenge_choose.attributes = challenge_choose.attributes.dup.except("id","challenge_id") new_challenge_choose.challenge_id = new_challenge.id new_challenge_choose.save! # 每一题的选项 if challenge_choose.challenge_questions.present? challenge_choose.challenge_questions.each do |challenge_question| new_challenge_question = ChallengeQuestion.new new_challenge_question.attributes = challenge_question.attributes.dup.except("id","challenge_choose_id") new_challenge_question.challenge_choose_id = new_challenge_choose.id new_challenge_question.save! end end # 每一题的知识标签 st_challenge_tags = ChallengeTag.where(:challenge_id => challenge.id, :challenge_choose_id => challenge_choose.id) if st_challenge_tags.present? st_challenge_tags.each do |st_challenge_tag| ChallengeTag.create!(:challenge_id => new_challenge.id, :name => st_challenge_tag.try(:name), :challenge_choose_id => new_challenge_choose.id) end end end end end end end # 中间层创建测试集 #shixun_modify_status_publish(new_shixun, 1) flash[:notice] = "实训复制成功" redirect_to shixun_challenges_path(new_shixun) rescue Exception => e logger.info("copy shixun failed ##{e.message}") flash[:notice] = "#{e.message}" g.delete_project(gshixun.id) if gshixun.try(:id).present? # 异常后,如果已经创建了版本库需要删除该版本库 redirect_to shixun_challenges_path(@shixun) raise ActiveRecord::Rollback end end end def fork_list @shixuns = Shixun.where(:fork_from => @shixun.id) @shixuns_count = @shixuns.count @limit = 16 @is_remote = true @shixun_pages = Paginator.new @shixuns_count, @limit, params['page'] || 1 @offset ||= @shixun_pages.offset @shixuns = paginateHelper @shixuns, @limit end def show # 如果是从TPI中退出,则更新TPI时间 # 更新时间是为了TPM端显示的更新,退出实训及访问实训的时候会更新 if params[:exit] && !User.current.admin? @shixun.myshixuns.where(:user_id => User.current).first.update_column(:updated_at, Time.now) end respond_to do |format| format.html{redirect_to shixun_challenges_path(@shixun)} end end def generate_identifier code = DCODES.sample(8).join return generate_identifier if Shixun.where(identifier: code).present? code end def down_generate_identifier type if type == "game" code = DCODES.sample(12).join return down_generate_identifier(type) if Game.where(identifier: code).present? elsif type == "myshixun" code = DCODES.sample(10).join return down_generate_identifier(type) if Myshixun.where(identifier: code).present? end code end def edit end def update @shixun.attributes = params[:shixun] #@shixun.language = params[:language] @shixun.trainee = params[:trainee] @shixun.webssh = params[:webssh].to_i @shixun.multi_webssh = @shixun.webssh == 2 && params[:multi_webssh] == "1" ? 1 : 0 @shixun.vnc = params[:vnc].to_i @shixun.can_copy = params[:can_copy].to_i @shixun.test_set_permission = params[:test_set_permission].to_i @shixun.code_hidden = params[:code_hidden].to_i @shixun.task_pass = params[:task_pass].to_i @shixun.mirror_script_id = params[:mirror_script].to_i @shixun.hide_code = params[:hide_code].to_i @shixun.forbid_copy = params[:forbid_copy].to_i if params[:public_degree] use_scope = params[:public_degree].to_i else use_scope = @shixun.use_scope end mirror_ids = (@shixun.shixun_mirror_repositories.blank? ? [] : @shixun.shixun_mirror_repositories.map(&:id)) update_miiror_id = [] @shixun.shixun_mirror_repositories.destroy_all if params[:main_type].present? update_miiror_id << params[:main_type].to_i ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => params[:main_type]) end if params[:small_type].present? sub_mirrors = params[:small_type].split(",").map(&:to_i) sub_mirrors.each do |mirror| update_miiror_id << mirror ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror) end end ActiveRecord::Base.transaction do begin @shixun.save! @shixun.shixun_schools.delete_all if params[:scope_partment].present? && use_scope == 1 arr = [] @shixun.schools ids = School.where(:name => params[:scope_partment]).map(&:id).uniq ids.each do |id| arr << { :school_id => id, :shixun_id => @shixun.id } end ShixunSchool.create!(arr) else use_scope = 0 end @shixun.update_attributes!(:use_scope => use_scope) rescue @error = "实训保存失败" raise ActiveRecord::Rollback end end respond_to do |format| format.js format.html{redirect_to settings_shixun_path(@shixun)} end end def shixun_discuss if User.current.logged? render "/common/index", :layout => false else redirect_to signin_path end # @discusses = @shixun.discusses.reorder("created_at desc") # @discusses_num = @discusses.count # @discusses = get_no_children_comments_all @discusses # @discusses_count = @discusses.count # @limit = 15 # @is_remote = true # @discusses_pages = Paginator.new @discusses_count, @limit, params['page'] || 1 # @offset ||= @discusses_pages.offset # @discusses = paginateHelper @discusses, @limit # #@game_challenge = params[:challenge_id].blank? ? Challenge.find(@discusses.first.try(:challenge_id)) : Challenge.find(params[:challenge_id]) # challenge_id = params[:challenge_id].nil? ? @shixun.challenges.first.try(:id) : params[:challenge_id].to_i # @game_challenge = Challenge.find(challenge_id) # @praise = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and praise_or_tread=? and user_id=?", @game_challenge.id, @game_challenge.class.to_s, 1, User.current.id).first # @tread = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and praise_or_tread=? and user_id=?", @game_challenge.id, @game_challenge.class.to_s, 0, User.current.id).first # @praise_count = PraiseTread.where(:praise_tread_object_id => @game_challenge.id, :praise_tread_object_type => "Challenge", :praise_or_tread => 1).count # @tread_count = PraiseTread.where(:praise_tread_object_id => @game_challenge.id, :praise_tread_object_type => "Challenge", :praise_or_tread => 0).count # # respond_to do |format| # format.js # format.html # end end def destroy ActiveRecord::Base.transaction do g = Gitlab.client g.delete_project(@shixun.gpid) if @shixun.try(:gpid).present? apply_record = ApplyAction.where(:container_id => @shixun.id, :container_type => "ApplyShixun") apply_record.delete_all if apply_record #HomeworkCommonsShixuns.where(:shixun_id => @shixun).delete_all # 关联删报错,后续解决 @shixun.update_attribute(:status, -1) respond_to do |format| if params[:come_from] == "admin" format.html{ redirect_to shixuns_managements_path } else format.html{ redirect_to user_path(User.current) } end format.js end end end def settings @edit = params[:edit] @repository = Repository.where(:shixun_id => @shixun, :type => "Repository::Gitlab").first @main_type = MirrorRepository.published_main_mirror @small_type = MirrorRepository.published_small_mirror @shixun_main_mirror = @shixun.mirror_repositories.published_main_mirror.first # 权限 @power = (@shixun.status < 2 ? true : ( User.current.admin? ? true : false)) # unless @repository.nil? # gitlab_address = Redmine::Configuration['gitlab_address'] # login = User.find_by_mail("educoder@163.com").try(:login) # @repos_url = gitlab_url @shixun # end respond_to do |format| format.html format.js end end # 添加实训脚本 def add_script if @shixun.update_attribute(:script, params[:shixun_script]) @notice = "脚本添加成功" else @notice = "脚本添加失败" end end def get_mirror_script mirror = MirrorRepository.find(params[:mirror_id]) script = mirror.mirror_scripts if mirror render :json => script end def get_script_contents if params[:script_id].to_i == -1 render :json => {contents: "", description: ""} else mirrir_script = MirrorScript.find(params[:script_id]) script = mirrir_script.try(:script) description = mirrir_script.try(:description) script = modify_shixun_script @shixun, script render :json => {contents: script, description: description} end end def get_common_script shixun_script = PlatformSample.where(:samples_type => "script").first.try(:contents) compile = params[:compile] execute = params[:executive] if shixun_script shixun_script = compile.blank? ? shixun_script.gsub("COMPILEFUNCTION", "").gsub("CHALLENGEFIELPATH", "") : shixun_script.gsub("COMPILEFUNCTION", "#{compile_command}").gsub("COMPILECOMMAND", "#{compile}") shixun_script = execute.blank? ? shixun_script.gsub("EXECUTEFUNCTION", "") : shixun_script.gsub("EXECUTECOMMAND", "#{execute}") shixun_script = modify_shixun_script @shixun, shixun_script end render :json => {contents: shixun_script} end # 创建实训job def shixun_job_create if @shixun.challenges.count == 0 @notice = "实训开启失败:请先发布实训任务" return elsif Repository.where(:shixun_id => @shixun.id, :type => "Repository::Gitlab").count == 0 @notice = "实训开启失败:请先创建版本库" return end @shixun.update_attribute(:status, 1) end # 更新实训job def shixun_job_update # jobName = "#{@shixun.id}" # pipeLine = "#{Base64.encode64(@shixun.script)}" # uri = URI("http://123.59.135.74:9999/jenkins-exec/api/updateJob") # params = {jobName: jobName, pipeLine: pipeLine} # res = uri_exec uri, params training_shixun_notice res if res['code'] == 0 @shixun.update_attribute(:status, 1) end end # Find shixun of id params[:id] def find_shixun @shixun = Shixun.find_by_identifier(params[:id]) render_404 if @shixun.nil? || @shixun.status == -1 rescue ActiveRecord::RecordNotFound render_404 end def monitor_filter if User.current.id == @shixun.user_id @notice = l(:label_shixun_mine) else if has_exec_cur_shixun(@shixun) @notice = l(:label_shixun_had_forked) @had_exec = true else @notice = l(:label_shixun_exec) @had_exec = false end end end # 实训行为操作 def operation @is_subject = params[:is_subject] @myshixun = Myshixun.where(:id => params[:myshixun_id]).first @is_modify = ShixunModify.where(:myshixun_id => params[:myshixun_id], :shixun_id => @shixun.try(:id), :status => 1).first @mail = User.current.mail.blank? # @challenge_count = @shixun.challenges.count # @git_commit = Gitlab.client.trees(@shixun.gpid).count.to_i @choice_shixun = @shixun.is_choice_type unfinish_challenge = @shixun.challenges.where(:path => nil, :st => 0) # 学员任务文件是否为空 if unfinish_challenge.count > 0 && !@shixun.is_choice_type @pos = [] unfinish_challenge.each do |challenge| @pos << challenge.position end @pos = @pos.join(",") end end # 实训的发送至课堂:搜索课堂 def search_user_courses @user = User.current if !params[:search].nil? search = "%#{params[:search].to_s.strip.downcase}%" @courses = @user.courses.not_deleted_not_end.where("#{Course.table_name}.name like :p",:p=>search).select{|course| @user.has_teacher_role(course)} else @courses = @user.courses.not_deleted_not_end.select{|course| @user.has_teacher_role(course)} end @pages = Paginator.new @courses.count, 8, params['page'] || 1 @offset ||= @pages.offset @courses = paginateHelper @courses, 8 respond_to do |format| format.js end end # 将实训发送到课程 def send_to_course course = Course.where(:id => params[:course]).first if course.present? homework = HomeworkCommon.new homework.name = @shixun.name homework.description = @shixun.description homework.anonymous_comment = 1 homework.homework_type = 4 homework.late_penalty = 0 homework.teacher_priority = 1 homework.user_id = User.current.id homework.course_id = params[:course] homework_detail_manual = HomeworkDetailManual.new homework_detail_manual.te_proportion = 1.0 homework_detail_manual.ta_proportion = 0 homework_detail_manual.comment_status = 0 homework_detail_manual.evaluation_num = 0 homework_detail_manual.absence_penalty = 0 homework.homework_detail_manual = homework_detail_manual if homework.save! homework_detail_manual.save if homework_detail_manual HomeworkCommonsShixuns.create(:homework_common_id => homework.id, :shixun_id => @shixun.id) create_shixun_homework_cha_setting homework, @shixun create_works_list homework redirect_to homework_common_index_path(:course => course.id, :homework_type => 4) end else render_404 end end private def view_allow shixun_view_allow @shixun end # REDO: 新增类型copy的时候 # 复制项目 # gshixun --> gitlab project # CODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z) def copy_myshixun shixun, gshixun myshixun = Myshixun.new # myshixun.attributes = shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier","propaedeutics") myshixun.shixun_id = shixun.id myshixun.user_id = User.current.id myshixun.gpid = gshixun.id shixun_tomcat = Redmine::Configuration['shixun_tomcat'] if myshixun.save! # 为了避免fork多次的问题 if gshixun.try(:name).include?("-") logger.info("***** gshixun name is #{gshixun.try(:name)}") @g.delete_project(gshixun.id) gshixun = @g.project(myshixun.gpid) myshixun.update_column(:gpid, gshixun.id) end code = down_generate_identifier("myshixun") myshixun.update_attribute(:identifier, code) rep = Repository.new(:myshixun_id => myshixun.id, :identifier => gshixun.name,:project_id => -1, :shixun_id => -2) rep.type = "Repository::Gitlab" rep.save! login = User.find_by_mail("educoder@163.com").try(:login) rep_url = Base64.urlsafe_encode64(gitlab_url shixun) uri = "#{shixun_tomcat}/bridge/game/openGameInstance" params = {tpiID: "#{myshixun.id}", tpmID: "#{shixun.id}", instanceGitURL:rep_url, operationEnvironment:"#{shixun.try(:language)}"} logger.info("openGameInstance params is #{params}") res = uri_exec uri, params if (res && res['code'].to_i != 0) raise("实训云平台繁忙(繁忙等级:83)") end return myshixun end end def training_shixun_notice res if res['code'] == 0 @notice = "实训开启成功" else @notice = res['msg'].nil? ? "实训开启失败" : res['msg'] end end # 实训管理员或者超级管理员 def require_manager render_403 unless User.current.manager_of_shixun?(@shixun) end def validation_email render_403 if User.current.mail.blank? end def validate_shixun end end