commit
c9526a57eb
@ -0,0 +1,86 @@
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
|
||||
var $container = $('.edit-laboratory-setting-container');
|
||||
var $form = $container.find('.edit_laboratory');
|
||||
|
||||
$('.logo-item-left').on("change", 'input[type="file"]', function () {
|
||||
var $fileInput = $(this);
|
||||
var file = this.files[0];
|
||||
var imageType = /image.*/;
|
||||
if (file && file.type.match(imageType)) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
var $box = $fileInput.parent();
|
||||
$box.find('img').attr('src', reader.result).css('display', 'block');
|
||||
$box.addClass('has-img');
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
}
|
||||
});
|
||||
|
||||
createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
|
||||
|
||||
$form.validate({
|
||||
errorElement: 'span',
|
||||
errorClass: 'danger text-danger',
|
||||
errorPlacement:function(error,element){
|
||||
if(element.parent().hasClass("input-group")){
|
||||
element.parent().after(error);
|
||||
}else{
|
||||
element.after(error)
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
identifier: {
|
||||
required: true,
|
||||
checkSite: true
|
||||
},
|
||||
name: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
$.validator.addMethod("checkSite",function(value,element,params){
|
||||
var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
|
||||
return this.optional(element)||(checkSite.test(value + '.educoder.com'));
|
||||
},"域名不合法!");
|
||||
|
||||
$form.on('click', '.submit-btn', function(){
|
||||
$form.find('.submit-btn').attr('disabled', 'disabled');
|
||||
$form.find('.error').html('');
|
||||
var valid = $form.valid();
|
||||
|
||||
$('input[name="navbar[][name]"]').each(function(_, e){
|
||||
var $ele = $(e);
|
||||
if($ele.val() === undefined || $ele.val().length === 0){
|
||||
$ele.addClass('danger text-danger');
|
||||
valid = false;
|
||||
} else {
|
||||
$ele.removeClass('danger text-danger');
|
||||
}
|
||||
});
|
||||
|
||||
if(!valid) return;
|
||||
$.ajax({
|
||||
method: 'PATCH',
|
||||
dataType: 'json',
|
||||
url: $form.attr('action'),
|
||||
data: new FormData($form[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);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
@ -0,0 +1,164 @@
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($('body.admins-laboratories-index-page').length > 0) {
|
||||
var $searchContainer = $('.laboratory-list-form');
|
||||
var $searchForm = $searchContainer.find('form.search-form');
|
||||
var $list = $('.laboratory-list-container');
|
||||
|
||||
// ============== 新建 ===============
|
||||
var $modal = $('.modal.admin-create-laboratory-modal');
|
||||
var $form = $modal.find('form.admin-create-laboratory-form');
|
||||
var $schoolSelect = $modal.find('.school-select');
|
||||
|
||||
$form.validate({
|
||||
errorElement: 'span',
|
||||
errorClass: 'danger text-danger',
|
||||
rules: {
|
||||
school_id: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
school_id: {
|
||||
required: '请选择所属单位'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// modal ready fire
|
||||
$modal.on('show.bs.modal', function () {
|
||||
$schoolSelect.select2('val', ' ');
|
||||
});
|
||||
|
||||
// ************** 学校选择 *************
|
||||
var matcherFunc = function(params, data){
|
||||
if ($.trim(params.term) === '') {
|
||||
return data;
|
||||
}
|
||||
if (typeof data.text === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data.name && data.name.indexOf(params.term) > -1) {
|
||||
var modifiedData = $.extend({}, data, true);
|
||||
return modifiedData;
|
||||
}
|
||||
|
||||
// Return `null` if the term should not be displayed
|
||||
return null;
|
||||
};
|
||||
|
||||
var defineSchoolSelect = function(schools) {
|
||||
$schoolSelect.select2({
|
||||
theme: 'bootstrap4',
|
||||
placeholder: '请选择单位',
|
||||
minimumInputLength: 1,
|
||||
data: schools,
|
||||
templateResult: function (item) {
|
||||
if(!item.id || item.id === '') return item.text;
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function(item){
|
||||
if (item.id) {
|
||||
$('#school_id').val(item.id);
|
||||
}
|
||||
return item.name || item.text;
|
||||
},
|
||||
matcher: matcherFunc
|
||||
});
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/api/schools/for_option.json',
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
defineSchoolSelect(data.schools);
|
||||
}
|
||||
});
|
||||
|
||||
$modal.on('click', '.submit-btn', function(){
|
||||
$form.find('.error').html('');
|
||||
|
||||
if ($form.valid()) {
|
||||
var url = $form.data('url');
|
||||
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
dataType: 'json',
|
||||
url: url,
|
||||
data: $form.serialize(),
|
||||
success: function(){
|
||||
$.notify({ message: '创建成功' });
|
||||
$modal.modal('hide');
|
||||
|
||||
setTimeout(function(){
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
},
|
||||
error: function(res){
|
||||
var data = res.responseJSON;
|
||||
$form.find('.error').html(data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============= 添加管理员 ==============
|
||||
var $addMemberModal = $('.admin-add-laboratory-user-modal');
|
||||
var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
|
||||
var $memberSelect = $addMemberModal.find('.laboratory-user-select');
|
||||
var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
|
||||
|
||||
$addMemberModal.on('show.bs.modal', function(event){
|
||||
var $link = $(event.relatedTarget);
|
||||
var laboratoryId = $link.data('laboratory-id');
|
||||
$laboratoryIdInput.val(laboratoryId);
|
||||
|
||||
$memberSelect.select2('val', ' ');
|
||||
});
|
||||
|
||||
$memberSelect.select2({
|
||||
theme: 'bootstrap4',
|
||||
placeholder: '请输入要添加的管理员姓名',
|
||||
multiple: true,
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
delay: 500,
|
||||
url: '/admins/users',
|
||||
dataType: 'json',
|
||||
data: function(params){
|
||||
return { name: params.term };
|
||||
},
|
||||
processResults: function(data){
|
||||
return { results: data.users }
|
||||
}
|
||||
},
|
||||
templateResult: function (item) {
|
||||
if(!item.id || item.id === '') return item.text;
|
||||
return item.real_name;
|
||||
},
|
||||
templateSelection: function(item){
|
||||
if (item.id) {
|
||||
}
|
||||
return item.real_name || item.text;
|
||||
}
|
||||
});
|
||||
|
||||
$addMemberModal.on('click', '.submit-btn', function(){
|
||||
$addMemberForm.find('.error').html('');
|
||||
|
||||
var laboratoryId = $laboratoryIdInput.val();
|
||||
var memberIds = $memberSelect.val();
|
||||
if (laboratoryId && memberIds && memberIds.length > 0) {
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
dataType: 'script',
|
||||
url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
|
||||
data: { user_ids: memberIds }
|
||||
});
|
||||
} else {
|
||||
$addMemberModal.modal('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -0,0 +1,99 @@
|
||||
.admins-laboratories-index-page {
|
||||
.laboratory-list-table {
|
||||
.member-container {
|
||||
.laboratory-user {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.laboratory-user-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
padding: 2px 5px;
|
||||
margin: 2px 2px;
|
||||
border: 1px solid #91D5FF;
|
||||
background-color: #E6F7FF;
|
||||
color: #91D5FF;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.admins-laboratory-settings-show-page, .admins-laboratory-settings-update-page {
|
||||
.edit-laboratory-setting-container {
|
||||
.logo-item {
|
||||
display: flex;
|
||||
|
||||
&-img {
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
&-upload {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: #F5F5F5;
|
||||
border: 1px solid #E5E5E5;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
left: 39px;
|
||||
width: 2px;
|
||||
height: 26px;
|
||||
background: #E5E5E5;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 39px;
|
||||
left: 27px;
|
||||
width: 26px;
|
||||
height: 2px;
|
||||
background: #E5E5E5;
|
||||
}
|
||||
}
|
||||
|
||||
&-left {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
||||
&.has-img {
|
||||
.logo-item-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.logo-item-upload {
|
||||
display: block;
|
||||
background: rgba(145, 145, 145, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #777777;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: #23272B;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
class Admins::LaboratoriesController < Admins::BaseController
|
||||
def index
|
||||
params[:sort_by] = params[:sort_by].presence || 'id'
|
||||
params[:sort_direction] = params[:sort_direction].presence || 'desc'
|
||||
|
||||
laboratories = Admins::LaboratoryQuery.call(params)
|
||||
@laboratories = paginate laboratories.preload(:school, :laboratory_users)
|
||||
end
|
||||
|
||||
def create
|
||||
Admins::CreateLaboratoryService.call(create_params)
|
||||
render_ok
|
||||
rescue Admins::CreateLaboratoryService::Error => ex
|
||||
render_error(ex.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
current_laboratory.destroy!
|
||||
|
||||
render_delete_success
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_laboratory
|
||||
@_current_laboratory ||= Laboratory.find(params[:id])
|
||||
end
|
||||
|
||||
def create_params
|
||||
params.permit(:school_id)
|
||||
end
|
||||
end
|
@ -0,0 +1,20 @@
|
||||
class Admins::LaboratorySettingsController < Admins::BaseController
|
||||
def show
|
||||
@laboratory = current_laboratory
|
||||
end
|
||||
|
||||
def update
|
||||
Admins::SaveLaboratorySettingService.call(current_laboratory, form_params)
|
||||
render_ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_laboratory
|
||||
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
|
||||
end
|
||||
|
||||
def form_params
|
||||
params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
class Admins::LaboratoryUsersController < Admins::BaseController
|
||||
helper_method :current_laboratory
|
||||
|
||||
def create
|
||||
Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
|
||||
current_laboratory.reload
|
||||
end
|
||||
|
||||
def destroy
|
||||
@laboratory_user = current_laboratory.laboratory_users.find_by(user_id: params[:user_id])
|
||||
@laboratory_user.destroy! if @laboratory_user.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_laboratory
|
||||
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
|
||||
end
|
||||
end
|
@ -0,0 +1,22 @@
|
||||
class BindUsersController < ApplicationController
|
||||
before_action :require_login
|
||||
|
||||
def create
|
||||
user = CreateBindUserService.call(current_user, create_params)
|
||||
successful_authentication(user) if user.id != current_user.id
|
||||
|
||||
render_ok
|
||||
rescue ApplicationService::Error => ex
|
||||
render_error(ex.message)
|
||||
end
|
||||
|
||||
def new_user
|
||||
current_user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.permit(:username, :password, :type, :not_bind)
|
||||
end
|
||||
end
|
@ -0,0 +1,15 @@
|
||||
module LaboratoryHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
helper_method :default_setting
|
||||
end
|
||||
|
||||
def current_laboratory
|
||||
@_current_laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
|
||||
end
|
||||
|
||||
def default_setting
|
||||
@_default_setting ||= LaboratorySetting.find_by(laboratory_id: 1)
|
||||
end
|
||||
end
|
@ -0,0 +1,69 @@
|
||||
module LoginHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def edu_setting(name)
|
||||
EduSetting.get(name)
|
||||
end
|
||||
|
||||
def autologin_cookie_name
|
||||
edu_setting('autologin_cookie_name').presence || 'autologin'
|
||||
end
|
||||
|
||||
def set_autologin_cookie(user)
|
||||
token = Token.get_or_create_permanent_login_token(user, "autologin")
|
||||
cookie_options = {
|
||||
:value => token.value,
|
||||
:expires => 1.month.from_now,
|
||||
:path => '/',
|
||||
:secure => false,
|
||||
:httponly => true
|
||||
}
|
||||
if edu_setting('cookie_domain').present?
|
||||
cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
|
||||
end
|
||||
cookies[autologin_cookie_name] = cookie_options
|
||||
Rails.logger.info("cookies is #{cookies}")
|
||||
end
|
||||
|
||||
def successful_authentication(user)
|
||||
Rails.logger.info("id: #{user&.id} Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}")
|
||||
# Valid user
|
||||
self.logged_user = user
|
||||
|
||||
# generate a key and set cookie if autologin
|
||||
set_autologin_cookie(user)
|
||||
|
||||
UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip)
|
||||
user.update_column(:last_login_on, Time.now)
|
||||
# 注册完成后有一天的试用申请(先去掉)
|
||||
# UserDayCertification.create(user_id: user.id, status: 1)
|
||||
end
|
||||
|
||||
def logout_user
|
||||
if User.current.logged?
|
||||
if autologin = cookies.delete(autologin_cookie_name)
|
||||
User.current.delete_autologin_token(autologin)
|
||||
end
|
||||
User.current.delete_session_token(session[:tk])
|
||||
self.logged_user = nil
|
||||
end
|
||||
session[:user_id] = nil
|
||||
end
|
||||
|
||||
# Sets the logged in user
|
||||
def logged_user=(user)
|
||||
reset_session
|
||||
if user && user.is_a?(User)
|
||||
User.current = user
|
||||
start_user_session(user)
|
||||
else
|
||||
User.current = User.anonymous
|
||||
end
|
||||
end
|
||||
|
||||
def start_user_session(user)
|
||||
session[:user_id] = user.id
|
||||
session[:ctime] = Time.now.utc.to_i
|
||||
session[:atime] = Time.now.utc.to_i
|
||||
end
|
||||
end
|
@ -0,0 +1,20 @@
|
||||
class Oauth::BaseController < ActionController::Base
|
||||
include RenderHelper
|
||||
include LoginHelper
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
private
|
||||
|
||||
def session_user_id
|
||||
session[:user_id]
|
||||
end
|
||||
|
||||
def current_user
|
||||
@_current_user ||= User.find_by(id: session_user_id)
|
||||
end
|
||||
|
||||
def auth_hash
|
||||
request.env['omniauth.auth']
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
class Oauth::QQController < Oauth::BaseController
|
||||
def create
|
||||
user, new_user = Oauth::CreateOrFindQqAccountService.call(current_user, auth_hash)
|
||||
|
||||
successful_authentication(user)
|
||||
|
||||
render_ok(new_user: new_user)
|
||||
end
|
||||
end
|
@ -0,0 +1,11 @@
|
||||
class Oauth::WechatController < Oauth::BaseController
|
||||
def create
|
||||
user, new_user = Oauth::CreateOrFindWechatAccountService.call(current_user ,params)
|
||||
|
||||
successful_authentication(user)
|
||||
|
||||
render_ok(new_user: new_user)
|
||||
rescue Oauth::CreateOrFindWechatAccountService::Error => ex
|
||||
render_error(ex.message)
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class SettingsController < ApplicationController
|
||||
def show
|
||||
@laboratory = current_laboratory
|
||||
end
|
||||
end
|
@ -0,0 +1,50 @@
|
||||
module OmniAuth
|
||||
module Strategies
|
||||
class QQ < OmniAuth::Strategies::OAuth2
|
||||
option :client_options, {
|
||||
site: 'https://graph.qq.com',
|
||||
authorize_url: '/oauth2.0/authorize',
|
||||
token_url: '/oauth2.0/token'
|
||||
}
|
||||
|
||||
def request_phase
|
||||
super
|
||||
end
|
||||
|
||||
def authorize_params
|
||||
super.tap do |params|
|
||||
%w[scope client_options].each do |v|
|
||||
if request.params[v]
|
||||
params[v.to_sym] = request.params[v]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uid { raw_info['openid'].to_s }
|
||||
|
||||
info do
|
||||
{
|
||||
name: user_info['nickname'],
|
||||
nickname: user_info['nickname'],
|
||||
image: user_info['figureurl_qq_1']
|
||||
}
|
||||
end
|
||||
|
||||
extra do
|
||||
{ raw_info: user_info }
|
||||
end
|
||||
|
||||
def raw_info
|
||||
access_token.options[:mode] = :query
|
||||
@raw_info ||= access_token.get('/oauth2.0/me').parsed
|
||||
end
|
||||
|
||||
def user_info
|
||||
access_token.options[:mode] = :query
|
||||
params = { oauth_consumer_key: options.client_id, openid: raw_info['openid'], format: 'json' }
|
||||
@user_info ||= access_token.get('/user/get_user_info', params: params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,13 @@
|
||||
module WechatOauth
|
||||
class << self
|
||||
attr_accessor :appid, :secret, :scope, :base_url
|
||||
|
||||
def logger
|
||||
@_logger ||= STDOUT
|
||||
end
|
||||
|
||||
def logger=(l)
|
||||
@_logger = l
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,14 @@
|
||||
class WechatOauth::Error < StandardError
|
||||
attr_reader :code
|
||||
|
||||
def initialize(code, msg)
|
||||
super(msg)
|
||||
@code = code
|
||||
end
|
||||
|
||||
def message
|
||||
I18n.t("oauth.wechat.#{code}")
|
||||
rescue I18n::MissingTranslationData
|
||||
super
|
||||
end
|
||||
end
|
@ -0,0 +1,61 @@
|
||||
module WechatOauth::Service
|
||||
module_function
|
||||
|
||||
def request(method, url, params)
|
||||
WechatOauth.logger.info("[WechatOauth] [#{method.to_s.upcase}] #{url} || #{params}")
|
||||
|
||||
client = Faraday.new(url: WechatOauth.base_url)
|
||||
response = client.public_send(method, url, params)
|
||||
result = JSON.parse(response.body)
|
||||
|
||||
WechatOauth.logger.info("[WechatOauth] [#{response.status}] #{result}")
|
||||
|
||||
if result['errcode'].present? && result['errcode'].to_s != '0'
|
||||
raise WechatOauth::Error.new(result['errcode'], result['errmsg'])
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
|
||||
# response:
|
||||
# {
|
||||
# "access_token":"ACCESS_TOKEN",
|
||||
# "expires_in":7200,
|
||||
# "refresh_token":"REFRESH_TOKEN",
|
||||
# "openid":"OPENID",
|
||||
# "scope":"SCOPE",
|
||||
# "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
|
||||
# }
|
||||
def access_token(code)
|
||||
params = {
|
||||
appid: WechatOauth.appid,
|
||||
secret: WechatOauth.secret,
|
||||
code: code,
|
||||
grant_type: 'authorization_code'
|
||||
}
|
||||
|
||||
request(:get, '/sns/oauth2/access_token', params)
|
||||
end
|
||||
|
||||
# https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html
|
||||
# response:
|
||||
# {
|
||||
# "openid":"OPENID",
|
||||
# "nickname":"NICKNAME",
|
||||
# "sex":1,
|
||||
# "province":"PROVINCE",
|
||||
# "city":"CITY",
|
||||
# "country":"COUNTRY",
|
||||
# "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
|
||||
# "privilege":[
|
||||
# "PRIVILEGE1",
|
||||
# "PRIVILEGE2"
|
||||
# ],
|
||||
# "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
|
||||
#
|
||||
# }
|
||||
def user_info(access_token, openid)
|
||||
request(:get, '/sns/userinfo', access_token: access_token, openid: openid)
|
||||
end
|
||||
end
|
@ -0,0 +1,26 @@
|
||||
class Laboratory < ApplicationRecord
|
||||
belongs_to :school, optional: true
|
||||
|
||||
has_many :laboratory_users, dependent: :destroy
|
||||
has_many :users, through: :laboratory_users, source: :user
|
||||
|
||||
has_one :laboratory_setting, dependent: :destroy
|
||||
|
||||
validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
|
||||
|
||||
def site
|
||||
rails_env = EduSetting.get('rails_env')
|
||||
suffix = rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net'
|
||||
|
||||
identifier ? "#{identifier}#{suffix}" : ''
|
||||
end
|
||||
|
||||
def self.find_by_subdomain(subdomain)
|
||||
return if subdomain.blank?
|
||||
|
||||
rails_env = EduSetting.get('rails_env')
|
||||
subdomain = subdomain.slice(0, subdomain.size - rails_env.size - 1) if subdomain.end_with?(rails_env) # winse.dev => winse
|
||||
|
||||
find_by_identifier(subdomain)
|
||||
end
|
||||
end
|
@ -0,0 +1,54 @@
|
||||
class LaboratorySetting < ApplicationRecord
|
||||
belongs_to :laboratory
|
||||
|
||||
serialize :config, JSON
|
||||
|
||||
%i[name navbar footer].each do |method_name|
|
||||
define_method method_name do
|
||||
config&.[](method_name.to_s)
|
||||
end
|
||||
|
||||
define_method "#{method_name}=" do |value|
|
||||
self.config ||= {}
|
||||
config.[]=(method_name.to_s, value)
|
||||
end
|
||||
end
|
||||
|
||||
def login_logo_url
|
||||
logo_url('login')
|
||||
end
|
||||
|
||||
def nav_logo_url
|
||||
logo_url('nav')
|
||||
end
|
||||
|
||||
def tab_logo_url
|
||||
logo_url('tab')
|
||||
end
|
||||
|
||||
def default_navbar
|
||||
self.class.default_config[:navbar]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def logo_url(type)
|
||||
return nil unless Util::FileManage.exists?(self, type)
|
||||
Util::FileManage.source_disk_file_url(self, type)
|
||||
end
|
||||
|
||||
def self.default_config
|
||||
{
|
||||
name: nil,
|
||||
navbar: [
|
||||
{ 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
|
||||
{ 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
|
||||
{ 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false },
|
||||
{ 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
|
||||
{ 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
|
||||
{ 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
|
||||
],
|
||||
footer: nil
|
||||
}
|
||||
end
|
||||
end
|
@ -0,0 +1,4 @@
|
||||
class LaboratoryUser < ApplicationRecord
|
||||
belongs_to :laboratory
|
||||
belongs_to :user
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
class OpenUser < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
validates :uid, presence: true, uniqueness: { scope: :type }
|
||||
|
||||
def can_bind_cache_key
|
||||
"open_user:#{type}:#{uid}:can_bind"
|
||||
end
|
||||
end
|
@ -0,0 +1,3 @@
|
||||
class OpenUsers::QQ < OpenUser
|
||||
|
||||
end
|
@ -0,0 +1,3 @@
|
||||
class OpenUsers::Wechat < OpenUser
|
||||
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
class Admins::LaboratoryQuery < ApplicationQuery
|
||||
include CustomSortable
|
||||
|
||||
attr_reader :params
|
||||
|
||||
sort_columns :id, default_by: :id, default_direction: :desc
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
laboratories = Laboratory.all
|
||||
|
||||
keyword = strip_param(:keyword)
|
||||
if keyword.present?
|
||||
like_sql = 'schools.name LIKE :keyword OR laboratories.identifier LIKE :keyword'
|
||||
laboratories = laboratories.left_joins(:school).where(like_sql, keyword: "%#{keyword}%")
|
||||
end
|
||||
|
||||
custom_sort laboratories, params[:sort_by], params[:sort_direction]
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
class Admins::AddLaboratoryUserService < ApplicationService
|
||||
attr_reader :laboratory, :params
|
||||
|
||||
def initialize(laboratory, params)
|
||||
@laboratory = laboratory
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
columns = %i[]
|
||||
LaboratoryUser.bulk_insert(*columns) do |worker|
|
||||
Array.wrap(params[:user_ids]).compact.each do |user_id|
|
||||
next if laboratory.laboratory_users.exists?(user_id: user_id)
|
||||
|
||||
worker.add(laboratory_id: laboratory.id, user_id: user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,20 @@
|
||||
class Admins::CreateLaboratoryService < ApplicationService
|
||||
Error = Class.new(StandardError)
|
||||
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
raise Error, '单位不能为空' if params[:school_id].blank?
|
||||
raise Error, '该单位已存在' if Laboratory.exists?(school_id: params[:school_id])
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
laboratory = Laboratory.create!(school_id: params[:school_id])
|
||||
|
||||
laboratory.create_laboratory_setting!
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,51 @@
|
||||
class Admins::SaveLaboratorySettingService < ApplicationService
|
||||
attr_reader :laboratory, :laboratory_setting, :params
|
||||
|
||||
def initialize(laboratory, params)
|
||||
@params = params
|
||||
@laboratory = laboratory
|
||||
@laboratory_setting = laboratory.laboratory_setting
|
||||
end
|
||||
|
||||
def call
|
||||
ActiveRecord::Base.transaction do
|
||||
laboratory.identifier = strip params[:identifier]
|
||||
laboratory_setting.name = strip params[:name]
|
||||
laboratory_setting.navbar = navbar_config
|
||||
laboratory_setting.footer = strip params[:footer]
|
||||
|
||||
laboratory.save!
|
||||
laboratory_setting.save!
|
||||
|
||||
deal_logo_file
|
||||
end
|
||||
|
||||
laboratory
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def navbar_config
|
||||
params[:navbar].map do |nav|
|
||||
hash = {}
|
||||
hash[:name] = strip nav[:name]
|
||||
hash[:link] = strip nav[:link]
|
||||
hash[:hidden] = nav[:hidden].to_s == 0
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def deal_logo_file
|
||||
save_logo_file(params[:nav_logo], 'nav')
|
||||
save_logo_file(params[:login_logo], 'login')
|
||||
save_logo_file(params[:tab_logo], 'tab')
|
||||
end
|
||||
|
||||
def save_logo_file(file, type)
|
||||
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
|
||||
|
||||
file_path = Util::FileManage.source_disk_filename(laboratory_setting, type)
|
||||
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
|
||||
Util.write_file(file, file_path)
|
||||
end
|
||||
end
|
@ -1,3 +1,11 @@
|
||||
class ApplicationService
|
||||
include Callable
|
||||
|
||||
Error = Class.new(StandardError)
|
||||
|
||||
private
|
||||
|
||||
def strip(str)
|
||||
str.to_s.strip.presence
|
||||
end
|
||||
end
|
@ -0,0 +1,52 @@
|
||||
class CreateBindUserService < ApplicationService
|
||||
attr_reader :user, :params
|
||||
|
||||
def initialize(user, params)
|
||||
@user = user
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
raise Error, '系统错误' if open_user.blank?
|
||||
raise Error, '系统错误' unless can_bind_user?
|
||||
|
||||
if params[:not_bind].to_s == 'true'
|
||||
clear_can_bind_user_flag
|
||||
return user
|
||||
end
|
||||
|
||||
bind_user = User.try_to_login(params[:username], params[:password])
|
||||
raise Error, '用户名或者密码错误' if bind_user.blank?
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
open_user.user_id = bind_user.id
|
||||
open_user.save!
|
||||
|
||||
user.user_extension.delete
|
||||
user.delete
|
||||
end
|
||||
|
||||
clear_can_bind_user_flag
|
||||
|
||||
bind_user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def open_user
|
||||
@_open_user ||= begin
|
||||
case params[:type].to_s
|
||||
when 'wechat' then user.wechat_open_user
|
||||
when 'qq' then user.qq_open_user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_bind_user?
|
||||
Rails.cache.read(open_user.can_bind_cache_key).present?
|
||||
end
|
||||
|
||||
def clear_can_bind_user_flag
|
||||
Rails.cache.delete(open_user.can_bind_cache_key)
|
||||
end
|
||||
end
|
@ -0,0 +1,38 @@
|
||||
class Oauth::CreateOrFindQqAccountService < ApplicationService
|
||||
|
||||
attr_reader :user, :params
|
||||
|
||||
def initialize(user, params)
|
||||
@user = user
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
new_user = false
|
||||
# 存在该用户
|
||||
open_user = OpenUsers::QQ.find_by(uid: params['uid'])
|
||||
return [open_user.user, new_user] if open_user.present?
|
||||
|
||||
if user.blank? || !user.logged?
|
||||
new_user = true
|
||||
# 新用户
|
||||
login = User.generate_login('q')
|
||||
@user = User.new(login: login, nickname: params.dig('info', 'nickname'), type: 'User', status: User::STATUS_ACTIVE)
|
||||
end
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
if user.new_record?
|
||||
user.save!
|
||||
|
||||
gender = params.dig('extra', 'raw_info', 'gender') == '女' ? 1 : 0
|
||||
user.create_user_extension!(gender: gender)
|
||||
end
|
||||
|
||||
new_open_user = OpenUsers::QQ.create!(user: user, uid: params['uid'])
|
||||
|
||||
Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定
|
||||
end
|
||||
|
||||
[user, new_user]
|
||||
end
|
||||
end
|
@ -0,0 +1,57 @@
|
||||
class Oauth::CreateOrFindWechatAccountService < ApplicationService
|
||||
Error = Class.new(StandardError)
|
||||
|
||||
attr_reader :user, :params
|
||||
|
||||
def initialize(user, params)
|
||||
@user = user
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
code = params['code'].to_s.strip
|
||||
raise Error, 'Code不能为空' if code.blank?
|
||||
new_user = false
|
||||
|
||||
result = WechatOauth::Service.access_token(code)
|
||||
result = WechatOauth::Service.user_info(result['access_token'], result['openid'])
|
||||
|
||||
# 存在该用户
|
||||
open_user = OpenUsers::Wechat.find_by(uid: result['unionid'])
|
||||
return [open_user.user, new_user] if open_user.present?
|
||||
|
||||
if user.blank? || !user.logged?
|
||||
new_user = true
|
||||
# 新用户
|
||||
login = User.generate_login('w')
|
||||
@user = User.new(login: login, nickname: result['nickname'], type: 'User', status: User::STATUS_ACTIVE)
|
||||
end
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
if new_user
|
||||
user.save!
|
||||
|
||||
gender = result['sex'].to_i == 1 ? 0 : 1
|
||||
user.create_user_extension!(gender: gender)
|
||||
|
||||
# 下载头像
|
||||
avatar_path = Util::FileManage.source_disk_filename(user)
|
||||
Util.download_file(result['headimgurl'], avatar_path)
|
||||
end
|
||||
|
||||
new_open_user= OpenUsers::Wechat.create!(user: user, uid: result['unionid'])
|
||||
|
||||
Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定
|
||||
end
|
||||
|
||||
[user, new_user]
|
||||
rescue WechatOauth::Error => ex
|
||||
raise Error, ex.message
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def code
|
||||
params[:code].to_s.strip
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
<% define_admin_breadcrumbs do %>
|
||||
<% add_admin_breadcrumb('云上实验室') %>
|
||||
<% end %>
|
||||
|
||||
<div class="box search-form-container laboratory-list-form">
|
||||
<%= form_tag(admins_laboratories_path(unsafe_params), method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
|
||||
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-6 col-md-4 ml-3', placeholder: '学校名称/二级域名前缀检索') %>
|
||||
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
|
||||
<% end %>
|
||||
|
||||
<%= javascript_void_link '新建', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-laboratory-modal' } %>
|
||||
</div>
|
||||
|
||||
<div class="box laboratory-list-container">
|
||||
<%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
|
||||
</div>
|
||||
|
||||
<%= render 'admins/laboratories/shared/create_laboratory_modal' %>
|
||||
<%= render 'admins/laboratories/shared/add_laboratory_user_modal' %>
|
@ -0,0 +1 @@
|
||||
$('.laboratory-list-container').html("<%= j(render partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>");
|
@ -0,0 +1,30 @@
|
||||
<div class="modal fade admin-add-laboratory-user-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">添加管理员</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="admin-add-laboratory-user-form">
|
||||
<%= hidden_field_tag(:laboratory_id, nil) %>
|
||||
|
||||
<div class="form-group d-flex">
|
||||
<label class="col-form-label">管理员:</label>
|
||||
<div class="d-flex flex-column-reverse w-75">
|
||||
<select id="user_ids" name="user_ids" class="form-control laboratory-user-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error text-danger"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary submit-btn">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,28 @@
|
||||
<div class="modal fade admin-create-laboratory-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">新建云上实验室</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="admin-create-laboratory-form" data-url="<%= admins_laboratories_path %>">
|
||||
<div class="form-group d-flex">
|
||||
<label for="school_id" class="col-form-label">选择单位:</label>
|
||||
<div class="d-flex flex-column-reverse w-75">
|
||||
<select id="school_id" name="school_id" class="form-control school-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error text-danger"></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary submit-btn">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,40 @@
|
||||
<% school = laboratory.school %>
|
||||
<td class="text-left"><%= school&.name || 'EduCoder主站' %></td>
|
||||
<td class="text-left">
|
||||
<% if laboratory.identifier %>
|
||||
<%= link_to laboratory.site, "https://#{laboratory.site}", target: '_blank' %>
|
||||
<% else %>
|
||||
--
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if school && school.identifier.present? %>
|
||||
<%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
|
||||
<% else %>
|
||||
--
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="member-container">
|
||||
<div class="laboratory-user">
|
||||
<% laboratory.users.each do |user| %>
|
||||
<span class="laboratory-user-item laboratory-user-item-<%= user.id %>">
|
||||
<%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %>
|
||||
<%= link_to(admins_laboratory_laboratory_user_path(laboratory, user_id: user.id),
|
||||
method: :delete, remote: true, class: 'ml-1 delete-laboratory-user-action',
|
||||
data: { confirm: '确认删除吗?' }) do %>
|
||||
<i class="fa fa-close"></i>
|
||||
<% end %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</td>
|
||||
<td><%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %></td>
|
||||
<td class="action-container">
|
||||
<%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory) %>
|
||||
|
||||
<% if school.present? && laboratory.id != 1 %>
|
||||
<%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
|
||||
|
||||
<%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'delete-laboratory-action' %>
|
||||
<% end %>
|
||||
</td>
|
@ -0,0 +1,25 @@
|
||||
<table class="table table-hover text-center laboratory-list-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th width="20%" class="text-left">单位名称</th>
|
||||
<th width="16%" class="text-left">域名</th>
|
||||
<th width="10%">统计链接</th>
|
||||
<th width="22%">管理员</th>
|
||||
<th width="14%"><%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %></th>
|
||||
<th width="20%">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if laboratories.present? %>
|
||||
<% laboratories.each do |laboratory| %>
|
||||
<tr class="laboratory-item laboratory-item-<%= laboratory.id %>">
|
||||
<%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render 'admins/shared/no_data_for_table' %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>
|
@ -0,0 +1,4 @@
|
||||
$('.modal.admin-add-laboratory-user-modal').modal('hide');
|
||||
$.notify({ message: '操作成功' });
|
||||
|
||||
$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>")
|
@ -0,0 +1,2 @@
|
||||
$.notify({ message: '操作成功' });
|
||||
$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();
|
@ -0,0 +1,12 @@
|
||||
json.setting do
|
||||
setting = @laboratory.laboratory_setting
|
||||
|
||||
json.name setting.name || default_setting.name
|
||||
json.nav_logo_url setting.nav_logo_url || default_setting.nav_logo_url
|
||||
json.login_logo_url setting.login_logo_url || default_setting.login_logo_url
|
||||
json.tab_logo_url setting.tab_logo_url || default_setting.tab_logo_url
|
||||
|
||||
json.navbar setting.navbar || default_setting.navbar
|
||||
|
||||
json.footer setting.footer || default_setting.footer
|
||||
end
|
@ -1 +1,2 @@
|
||||
admins-mirror_scripts: 'admins-mirror_repositories'
|
||||
admins-mirror_scripts: 'admins-mirror_repositories'
|
||||
admins-laboratory_settings: 'admins-laboratories'
|
@ -0,0 +1,17 @@
|
||||
OmniAuth.config.add_camelization 'qq', 'QQ'
|
||||
|
||||
oauth_config = {}
|
||||
begin
|
||||
config = Rails.application.config_for(:configuration)
|
||||
oauth_config = config.dig('oauth', 'qq')
|
||||
raise 'oauth qq config missing' if oauth_config.blank?
|
||||
rescue => ex
|
||||
raise ex if Rails.env.production?
|
||||
|
||||
puts %Q{\033[33m [warning] qq oauth config or configuration.yml missing,
|
||||
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
|
||||
end
|
||||
|
||||
Rails.application.config.middleware.use OmniAuth::Builder do
|
||||
provider :qq, oauth_config['appid'], oauth_config['secret']
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
oauth_config = {}
|
||||
begin
|
||||
config = Rails.application.config_for(:configuration)
|
||||
oauth_config = config.dig('oauth', 'wechat')
|
||||
raise 'oauth wechat config missing' if oauth_config.blank?
|
||||
rescue => ex
|
||||
raise ex if Rails.env.production?
|
||||
|
||||
puts %Q{\033[33m [warning] wechat oauth config or configuration.yml missing,
|
||||
please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
|
||||
end
|
||||
|
||||
WechatOauth.appid = oauth_config['appid']
|
||||
WechatOauth.secret = oauth_config['secret']
|
||||
WechatOauth.scope = oauth_config['scope']
|
||||
WechatOauth.base_url = oauth_config['base_url']
|
||||
WechatOauth.logger = Rails.logger
|
@ -0,0 +1,7 @@
|
||||
zh-CN:
|
||||
activerecord:
|
||||
models:
|
||||
laboratory: ''
|
||||
attributes:
|
||||
laboratory:
|
||||
identifier: '二级域名'
|
@ -0,0 +1,4 @@
|
||||
'zh-CN':
|
||||
oauth:
|
||||
wechat:
|
||||
'40029': '授权已失效,请重新授权'
|
@ -0,0 +1,14 @@
|
||||
class CreateOpenUsers < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :open_users do |t|
|
||||
t.references :user
|
||||
|
||||
t.string :type
|
||||
t.string :uid
|
||||
|
||||
t.timestamps
|
||||
|
||||
t.index [:type, :uid], unique: true
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,12 @@
|
||||
class CreateLaboratories < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :laboratories do |t|
|
||||
t.references :school
|
||||
t.string :identifier
|
||||
|
||||
t.timestamps
|
||||
|
||||
t.index :identifier, unique: true
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
class CreateLaboratoryUsers < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :laboratory_users do |t|
|
||||
t.references :laboratory
|
||||
t.references :user
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
class CreateLaboratorySettings < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :laboratory_settings do |t|
|
||||
t.references :laboratory
|
||||
|
||||
t.text :config
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,22 @@
|
||||
class InitEduCoderLaboratory < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
ActiveRecord::Base.transaction do
|
||||
laboratory = Laboratory.create!(id: 1, identifier: 'www')
|
||||
setting = laboratory.build_laboratory_setting
|
||||
footer = %Q{
|
||||
<p class="footer_con-p inline lineh-30 font-14">
|
||||
<span class="font-18 fl">©</span> 2019 EduCoder
|
||||
<a target="_blank" href="http://beian.miit.gov.cn/" class="ml15 mr15" style="color: rgb(136, 136, 136);">湘ICP备17009477号</a>
|
||||
<a target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=43019002000962" class="mr15" style="color: rgb(136, 136, 136);">
|
||||
<img class="vertical4" src="https://ali-cdn.educoder.net/react/build/static/media/beian.d0289dc0.png">湘公网安备43019002000962号
|
||||
</a>
|
||||
<a href="https://team.trustie.net" target="_blank" style="color: rgb(136, 136, 136);">Trustie</a>
|
||||
& IntelliDE inside.
|
||||
<span class="mr15">版权所有 湖南智擎科技有限公司</span></p>
|
||||
}
|
||||
config = setting.class.default_config.merge(name: 'EduCoder', footer: footer)
|
||||
setting.config = config
|
||||
setting.save!
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue