diff --git a/app/assets/javascripts/admins/competition_settings/index.js b/app/assets/javascripts/admins/competition_settings/index.js index f02f845c4..2b3c31190 100644 --- a/app/assets/javascripts/admins/competition_settings/index.js +++ b/app/assets/javascripts/admins/competition_settings/index.js @@ -5,8 +5,7 @@ $(document).on('turbolinks:load', function(){ autoclose: true, language: 'zh-CN', format: 'yyyy-mm-dd', - startDate: '2017-04-01', - endDate: '-1d' + startDate: '2017-04-01' }; var defineDateRangeSelect = function(element){ @@ -19,5 +18,74 @@ $(document).on('turbolinks:load', function(){ }; defineDateRangeSelect('.teaching-mode-date'); - } + defineDateRangeSelect('.competition-start-end-date'); + + var $basicForm = $('form.basic-setting-form'); + + $basicForm.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + name: "required", + subTitle: "required", + startTime: "required", + endTime: "required", + mode: "required", + identifier: "required" + } + }); + + // 保存按钮 + $basicForm.on('click', ".submit-btn", function(){ + $basicForm.find('.submit-btn').attr('disabled', 'disabled'); + $basicForm.find('.error').html(''); + var valid = $basicForm.valid(); + + if($("input[name='mode'[checked]]").val() == 2) { + var $courseId = $("input[name='course_id'"); + if($courseId.val() === undefined || $course_id.val().length === 0){ + $courseId.addClass('danger text-danger'); + valid = false; + } else { + $courseId.removeClass('danger text-danger'); + } + } else if ($("input[name='mode'[checked]]").val() == 4) { + var $techStartTime = $("input[name='teach_start_time'"); + var $techEndTime = $("input[name='teach_end_time'"); + if($techStartTime.val() === undefined || $techStartTime.val().length === 0){ + $techStartTime.addClass('danger text-danger'); + valid = false; + } else { + $techStartTime.removeClass('danger text-danger'); + } + + if($techEndTime.val() === undefined || $techEndTime.val().length === 0){ + $techEndTime.addClass('danger text-danger'); + valid = false; + } else { + $techEndTime.removeClass('danger text-danger'); + } + } + + if(!valid) return; + $.ajax({ + method: 'POST', + dataType: 'json', + url: $basicForm.attr('action'), + data: new FormData($basicForm[0]), + processData: false, + contentType: false, + success: function(data){ + $.notify({ message: '保存成功' }); + window.location.reload(); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + }, + complete: function(){ + $form.find('.submit-btn').attr('disabled', false); + } + }); + }); }); \ No newline at end of file diff --git a/app/controllers/competitions/competition_teams_controller.rb b/app/controllers/competitions/competition_teams_controller.rb index 6a4dbfd34..db4608807 100644 --- a/app/controllers/competitions/competition_teams_controller.rb +++ b/app/controllers/competitions/competition_teams_controller.rb @@ -60,7 +60,12 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController def index @personal = current_competition.personal? - admin_or_business? ? all_competition_teams : user_competition_teams + if admin_or_business? + all_competition_teams + user_competition_teams + else + user_competition_teams + end end def create @@ -113,8 +118,8 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController teams = teams.joins(users: { user_extension: :school }).where('schools.name LIKE ?', "%#{keyword}%") end - @count = teams.count - @teams = paginate(teams.includes(:user, users: { user_extension: :school })) + @all_count = teams.count + @all_teams = paginate(teams.includes(:user, users: { user_extension: :school })) end def user_competition_teams diff --git a/app/views/admins/competition_settings/index.html.erb b/app/views/admins/competition_settings/index.html.erb index 4c73609e4..4539a6e34 100644 --- a/app/views/admins/competition_settings/index.html.erb +++ b/app/views/admins/competition_settings/index.html.erb @@ -10,8 +10,36 @@ 基础设置
- <%= form_tag(admins_competition_competition_settings_path(unsafe_params), method: :post, class: 'basic-setting-form flex-1', remote: true) do %> + <%= simple_form_for(@competition, url: a_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %>
+
+
+ 主标题 +
+
+ <%= text_field_tag(:name, @competition.name, autocomplete: 'off', class: 'form-control', placeholder: '竞赛标题') %> +
+
+ +
+
+ 副标题 +
+
+ <%= text_field_tag(:sub_title, @competition.sub_title, autocomplete: 'off', class: 'form-control', placeholder: '竞赛副标题') %> +
+
+ +
+
+ 起止时间 +
+
+ <%= text_field_tag :start_time, @competition.start_time&.strftime('%Y-%m-%d'), autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '竞赛开始时间' %> + <%= text_field_tag :end_time, @competition.end_time&.strftime('%Y-%m-%d'), autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '竞赛截止时间' %> +
+
+
竞赛模式 @@ -42,12 +70,12 @@
- <%= text_field_tag :start_time, @competition.competition_mode_setting&.start_time, autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '统计数据的开始时间' %> - <%= text_field_tag :end_time, @competition.competition_mode_setting&.end_time, autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '统计数据的结束时间' %> + <%= text_field_tag :teach_start_time, @competition.competition_mode_setting&.start_time, autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '统计数据的开始时间' %> + <%= text_field_tag :teach_end_time, @competition.competition_mode_setting&.end_time, autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '统计数据的结束时间' %>
-
+
@@ -56,7 +84,7 @@
-
+
URL
@@ -65,7 +93,7 @@
-
+
主办方
@@ -74,7 +102,7 @@
-
+
奖金
@@ -86,16 +114,16 @@
-
+
- 获奖人数 + 奖项数
- <%= number_field_tag(:bonus, @competition.bonus, autocomplete: 'off', step: 1, min: 0, class: 'form-control', placeholder: '请输入总奖金额') %> + <%= number_field_tag(:awards_count, @competition.awards_count, autocomplete: 'off', step: 1, min: 0, class: 'form-control', placeholder: '请输入奖项数') %>
-
+
描述
@@ -104,7 +132,9 @@
-
+
+ +
@@ -112,7 +142,6 @@
- <% end %>
diff --git a/app/views/competitions/competition_teams/index.json.jbuilder b/app/views/competitions/competition_teams/index.json.jbuilder index d44fcdf1c..a86bfb30e 100644 --- a/app/views/competitions/competition_teams/index.json.jbuilder +++ b/app/views/competitions/competition_teams/index.json.jbuilder @@ -1,12 +1,11 @@ -json.count @count +json.count @all_count json.personal @personal json.competition_teams do - json.array! @teams.each do |team| + json.array! @all_teams&.each do |team| json.extract! team, :id, :name, :invite_code json.team_type team.en_team_type json.school_name team.user.school_name - - json.manage_permission current_user.id == team.user_id + json.created_at team.created_at.strftime("%Y-%m-%d %H:%M") json.creator do json.partial! 'users/user_simple', user: team.user @@ -25,3 +24,29 @@ json.competition_teams do end end end + +json.my_teams @teams.each do |team| + json.extract! team, :id, :name, :invite_code + json.team_type team.en_team_type + json.school_name team.user.school_name + + json.manage_permission current_user.id == team.user_id + + json.creator do + json.partial! 'users/user_simple', user: team.user + json.role team.team_members.find(&:creator?).en_role + end + + json.team_members do + json.array! team.team_members.each do |member| + json.partial! 'users/user_simple', user: member.user + json.user_id member.user_id + json.role member.en_role + json.identity member.user.identity + json.school_name member.user.school_name + json.student_id member.user.student_id + end + end +end + + diff --git a/public/react/src/common/UrlTool.js b/public/react/src/common/UrlTool.js index 3ad52f264..db97642b7 100644 --- a/public/react/src/common/UrlTool.js +++ b/public/react/src/common/UrlTool.js @@ -71,3 +71,18 @@ export function toPath(path) { export function getTaskUrlById(id) { return `/tasks/${id}` } + + +export function htmlEncode(str) { + var s = ""; + if (str.length === 0) { + return ""; + } + s = str.replace(/&/g, "&"); + s = s.replace(//g, ">"); + s = s.replace(/ /g, " "); + s = s.replace(/\'/g, "'");//IE下不支持实体名称 + s = s.replace(/\"/g, """); + return s; +} \ No newline at end of file diff --git a/public/react/src/common/educoder.js b/public/react/src/common/educoder.js index ec4659de7..73c707c9b 100644 --- a/public/react/src/common/educoder.js +++ b/public/react/src/common/educoder.js @@ -4,7 +4,7 @@ import { from } from '_array-flatten@2.1.2@array-flatten'; export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl , getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth - , getTaskUrlById as getTaskUrlById, TEST_HOST } from './UrlTool'; + , getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode } from './UrlTool'; export { default as queryString } from './UrlTool2'; export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC'; diff --git a/public/react/src/modules/forums/MemoDetail.js b/public/react/src/modules/forums/MemoDetail.js index cc206713b..7578eea6e 100644 --- a/public/react/src/modules/forums/MemoDetail.js +++ b/public/react/src/modules/forums/MemoDetail.js @@ -22,7 +22,7 @@ import {ImageLayerOfCommentHOC} from '../page/layers/ImageLayerOfCommentHOC' import MemoDetailKEEditor from './MemoDetailKEEditor' import MemoDetailMDEditor from './MemoDetailMDEditor' -import { bytesToSize, CBreadcrumb } from 'educoder' +import { bytesToSize, CBreadcrumb ,htmlEncode} from 'educoder' import { Tooltip } from 'antd' // import CBreadcrumb from '../courses/common/CBreadcrumb' @@ -246,6 +246,8 @@ class MemoDetail extends Component { if (commentContent) { commentContent = commentContent.replace(/(\n

\n\t
\n<\/p>)*$/g,''); } + + commentContent=htmlEncode(commentContent) axios.post(url, { parent_id: id, content: commentContent @@ -491,6 +493,7 @@ class MemoDetail extends Component { const url = `/memos/reply.json`; let { comments } = this.state; const user = this._getUser(); + content=htmlEncode(content) axios.post(url, { parent_id: memo.id, content: content diff --git a/public/react/src/modules/message/js/MessagChat.js b/public/react/src/modules/message/js/MessagChat.js index 34494a256..e320ff3a3 100644 --- a/public/react/src/modules/message/js/MessagChat.js +++ b/public/react/src/modules/message/js/MessagChat.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import "../css/messagemy.css" -import {getImageUrl,markdownToHTML} from 'educoder'; +import {getImageUrl,markdownToHTML,htmlEncode} from 'educoder'; import { Modal,Input,Icon,Tooltip,Spin} from 'antd'; import axios from 'axios'; import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; @@ -417,6 +417,7 @@ class MessagChat extends Component{ let contents=this.messageRef.current.getValue().trim(); const query = this.props.location.search; let target_ids = query.split('?target_ids='); + contents=htmlEncode(contents) let url = `/users/${this.props.match.params.userid}/private_messages.json`; axios.post(url, { target_id: target_ids[1], diff --git a/public/react/src/modules/message/messagemodal/WriteaprivateletterModal.js b/public/react/src/modules/message/messagemodal/WriteaprivateletterModal.js index dfa27ba9a..da0af0448 100644 --- a/public/react/src/modules/message/messagemodal/WriteaprivateletterModal.js +++ b/public/react/src/modules/message/messagemodal/WriteaprivateletterModal.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { Modal,Input,Icon,Tooltip,Spin} from 'antd'; import axios from 'axios'; // import '../../modules/user/common.css'; -import {getImageUrl} from 'educoder'; +import {getImageUrl,htmlEncode} from 'educoder'; //完善个人资料 class WriteaprivateletterModal extends Component { @@ -58,6 +58,7 @@ class WriteaprivateletterModal extends Component { //发送私信 SendprivatemessageAPI=(idvalue,contentvalue)=>{ const url =`/users/${this.props.current_user.user_id}/private_messages.json` + contentvalue=htmlEncode(contentvalue) let data={ target_id:idvalue, content:contentvalue,