forked from Trustie/forgeplus
merge from develop
This commit is contained in:
commit
534fdec9f2
272
Gemfile
272
Gemfile
|
|
@ -1,130 +1,142 @@
|
|||
source 'https://gems.ruby-china.com'
|
||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||
|
||||
gem 'rails', '~> 5.2.0'
|
||||
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
|
||||
gem 'puma', '~> 3.11'
|
||||
gem 'sass-rails', '~> 5.0'
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
|
||||
# gem 'coffee-rails', '~> 4.2'
|
||||
gem 'turbolinks', '~> 5'
|
||||
gem 'jbuilder', '~> 2.5'
|
||||
gem 'groupdate', '~> 4.1.0'
|
||||
gem 'chartkick'
|
||||
gem 'grape-entity', '~> 0.7.1'
|
||||
gem 'kaminari', '~> 1.1', '>= 1.1.1'
|
||||
|
||||
gem 'bootsnap', '>= 1.1.0', require: false
|
||||
|
||||
gem 'chinese_pinyin'
|
||||
|
||||
gem 'rack-cors'
|
||||
gem 'redis-rails'
|
||||
gem 'roo-xls'
|
||||
gem 'simple_xlsx_reader'
|
||||
|
||||
gem 'rubyzip'
|
||||
|
||||
gem 'spreadsheet'
|
||||
gem 'ruby-ole'
|
||||
# 导出为xlsx
|
||||
gem 'axlsx', '~> 3.0.0.pre'
|
||||
gem 'axlsx_rails', '~> 0.5.2'
|
||||
|
||||
gem 'oauth2'
|
||||
#导出为pdf
|
||||
gem 'pdfkit'
|
||||
gem 'wkhtmltopdf-binary'
|
||||
# gem 'request_store'
|
||||
#gem 'iconv'
|
||||
# markdown 转html
|
||||
gem 'redcarpet', '~> 3.4'
|
||||
|
||||
gem 'rqrcode', '~> 0.10.1'
|
||||
gem 'rqrcode_png'
|
||||
|
||||
gem 'acts-as-taggable-on', '~> 6.0'
|
||||
|
||||
# a tree structure
|
||||
gem 'ancestry'
|
||||
gem 'acts_as_list'
|
||||
gem 'omniauth-cas'
|
||||
|
||||
# profiler Middleware
|
||||
gem 'rack-mini-profiler'
|
||||
|
||||
# object-based searching
|
||||
gem 'ransack'
|
||||
|
||||
group :development, :test do
|
||||
gem 'rspec-rails', '~> 3.8'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'prettier'
|
||||
gem 'rubocop', '~> 0.52.0'
|
||||
gem 'solargraph', '~> 0.38.0'
|
||||
gem 'awesome_print'
|
||||
gem 'web-console', '>= 3.3.0'
|
||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||
gem 'spring'
|
||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||
gem "annotate", "~> 2.6.0"
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara', '>= 2.15', '< 4.0'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'chromedriver-helper'
|
||||
end
|
||||
|
||||
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||
|
||||
#编码检测
|
||||
gem 'rchardet', '~> 1.8'
|
||||
|
||||
# http client
|
||||
gem 'faraday', '~> 0.15.4'
|
||||
|
||||
# view
|
||||
gem 'active_decorator'
|
||||
gem 'bootstrap', '~> 4.3.1'
|
||||
gem 'jquery-rails'
|
||||
gem 'simple_form'
|
||||
gem 'font-awesome-sass', '4.7.0'
|
||||
|
||||
# i18n
|
||||
gem 'rails-i18n', '~> 5.1'
|
||||
|
||||
# job
|
||||
gem 'sidekiq'
|
||||
gem 'sinatra'
|
||||
gem "sidekiq-cron", "~> 1.1"
|
||||
|
||||
# batch insert
|
||||
gem 'bulk_insert'
|
||||
|
||||
# elasticsearch
|
||||
gem 'searchkick'
|
||||
|
||||
gem 'aasm'
|
||||
gem 'enumerize'
|
||||
|
||||
gem 'diffy'
|
||||
|
||||
gem 'deep_cloneable', '~> 3.0.0'
|
||||
|
||||
# oauth2
|
||||
gem 'omniauth', '~> 1.9.0'
|
||||
gem 'omniauth-oauth2', '~> 1.6.0'
|
||||
|
||||
# global var
|
||||
gem 'request_store'
|
||||
|
||||
# 敏感词汇
|
||||
gem 'harmonious_dictionary', '~> 0.0.1'
|
||||
|
||||
gem 'parallel', '~> 1.19', '>= 1.19.1'
|
||||
|
||||
gem 'letter_avatar'
|
||||
source 'https://gems.ruby-china.com'
|
||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||
|
||||
gem 'rails', '~> 5.2.0'
|
||||
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
|
||||
gem 'puma', '~> 3.11'
|
||||
gem 'sass-rails', '~> 5.0'
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
|
||||
# gem 'coffee-rails', '~> 4.2'
|
||||
gem 'turbolinks', '~> 5'
|
||||
gem 'jbuilder', '~> 2.5'
|
||||
gem 'groupdate', '~> 4.1.0'
|
||||
gem 'chartkick'
|
||||
gem 'grape-entity', '~> 0.7.1'
|
||||
gem 'kaminari', '~> 1.1', '>= 1.1.1'
|
||||
|
||||
gem 'bootsnap', '>= 1.1.0', require: false
|
||||
|
||||
gem 'chinese_pinyin'
|
||||
|
||||
gem 'rack-cors'
|
||||
gem 'redis-rails'
|
||||
gem 'roo-xls'
|
||||
gem 'simple_xlsx_reader'
|
||||
|
||||
gem 'rubyzip'
|
||||
|
||||
gem 'spreadsheet'
|
||||
gem 'ruby-ole'
|
||||
# 导出为xlsx
|
||||
gem 'axlsx', '~> 3.0.0.pre'
|
||||
gem 'axlsx_rails', '~> 0.5.2'
|
||||
|
||||
gem 'oauth2'
|
||||
#导出为pdf
|
||||
gem 'pdfkit'
|
||||
gem 'wkhtmltopdf-binary'
|
||||
# gem 'request_store'
|
||||
#gem 'iconv'
|
||||
# markdown 转html
|
||||
gem 'redcarpet', '~> 3.4'
|
||||
|
||||
gem 'rqrcode', '~> 0.10.1'
|
||||
gem 'rqrcode_png'
|
||||
|
||||
gem 'acts-as-taggable-on', '~> 6.0'
|
||||
|
||||
# a tree structure
|
||||
gem 'ancestry'
|
||||
gem 'acts_as_list'
|
||||
gem 'omniauth-cas'
|
||||
|
||||
# profiler Middleware
|
||||
gem 'rack-mini-profiler'
|
||||
|
||||
# object-based searching
|
||||
gem 'ransack'
|
||||
|
||||
group :development, :test do
|
||||
gem 'rspec-rails', '~> 3.8'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'prettier'
|
||||
gem 'rubocop', '~> 0.52.0'
|
||||
gem 'solargraph', '~> 0.38.0'
|
||||
gem 'awesome_print'
|
||||
gem 'web-console', '>= 3.3.0'
|
||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||
gem 'spring'
|
||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||
gem "annotate", "~> 2.6.0"
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara', '>= 2.15', '< 4.0'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'chromedriver-helper'
|
||||
end
|
||||
|
||||
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||
|
||||
#编码检测
|
||||
gem 'rchardet', '~> 1.8'
|
||||
|
||||
# http client
|
||||
gem 'faraday', '~> 0.15.4'
|
||||
|
||||
# view
|
||||
gem 'active_decorator'
|
||||
gem 'bootstrap', '~> 4.3.1'
|
||||
gem 'jquery-rails'
|
||||
gem 'simple_form'
|
||||
gem 'font-awesome-sass', '4.7.0'
|
||||
|
||||
# i18n
|
||||
gem 'rails-i18n', '~> 5.1'
|
||||
|
||||
# job
|
||||
gem 'sidekiq'
|
||||
gem 'sinatra'
|
||||
gem "sidekiq-cron", "~> 1.1"
|
||||
|
||||
# batch insert
|
||||
gem 'bulk_insert'
|
||||
|
||||
# elasticsearch
|
||||
gem 'searchkick'
|
||||
|
||||
gem 'aasm'
|
||||
gem 'enumerize'
|
||||
|
||||
gem 'diffy'
|
||||
|
||||
gem 'deep_cloneable', '~> 3.0.0'
|
||||
|
||||
# oauth2
|
||||
gem 'omniauth', '~> 1.9.0'
|
||||
gem 'omniauth-oauth2', '~> 1.6.0'
|
||||
gem "omniauth-github"
|
||||
gem "omniauth-rails_csrf_protection"
|
||||
gem 'omniauth-gitee', '~> 1.0.0'
|
||||
gem "omniauth-wechat-oauth2"
|
||||
|
||||
# global var
|
||||
gem 'request_store'
|
||||
|
||||
# 敏感词汇
|
||||
gem 'harmonious_dictionary', '~> 0.0.1'
|
||||
|
||||
gem 'parallel', '~> 1.19', '>= 1.19.1'
|
||||
|
||||
gem 'letter_avatar'
|
||||
|
||||
gem 'jwt'
|
||||
|
||||
gem 'doorkeeper'
|
||||
|
||||
gem 'doorkeeper-jwt'
|
||||
|
||||
gem 'gitea-client', '~> 0.10.6'
|
||||
|
|
@ -106,6 +106,10 @@ GEM
|
|||
activerecord (>= 3.1.0, < 7)
|
||||
diff-lcs (1.3)
|
||||
diffy (3.3.0)
|
||||
doorkeeper (5.5.1)
|
||||
railties (>= 5)
|
||||
doorkeeper-jwt (0.4.1)
|
||||
jwt (>= 2.1)
|
||||
e2mmap (0.1.0)
|
||||
elasticsearch (7.5.0)
|
||||
elasticsearch-api (= 7.5.0)
|
||||
|
|
@ -450,6 +454,8 @@ DEPENDENCIES
|
|||
chromedriver-helper
|
||||
deep_cloneable (~> 3.0.0)
|
||||
diffy
|
||||
doorkeeper
|
||||
doorkeeper-jwt
|
||||
enumerize
|
||||
faraday (~> 0.15.4)
|
||||
font-awesome-sass (= 4.7.0)
|
||||
|
|
@ -458,6 +464,7 @@ DEPENDENCIES
|
|||
harmonious_dictionary (~> 0.0.1)
|
||||
jbuilder (~> 2.5)
|
||||
jquery-rails
|
||||
jwt
|
||||
kaminari (~> 1.1, >= 1.1.1)
|
||||
letter_avatar
|
||||
listen (>= 3.0.5, < 3.2)
|
||||
|
|
|
|||
|
|
@ -1,136 +1,137 @@
|
|||
//= require rails-ujs
|
||||
//= require activestorage
|
||||
//= require turbolinks
|
||||
//= require jquery3
|
||||
//= require popper
|
||||
//= require bootstrap-sprockets
|
||||
//= require jquery.validate.min
|
||||
//= require additional-methods.min
|
||||
//= require bootstrap-notify
|
||||
//= require jquery.cookie.min
|
||||
//= require select2
|
||||
//= require moment.min
|
||||
//= require jquery.cxselect
|
||||
//= require bootstrap-datepicker
|
||||
//= require bootstrap-datetimepicker
|
||||
//= require bootstrap.viewer
|
||||
//= require jquery.mloading
|
||||
//= require jquery-confirm.min
|
||||
//= require common
|
||||
|
||||
//= require echarts
|
||||
//= require codemirror/lib/codemirror
|
||||
//= require codemirror/mode/shell/shell
|
||||
//= require editormd/editormd
|
||||
//= require editormd/languages/zh-tw
|
||||
//= require dragula/dragula
|
||||
|
||||
//= require_tree ./i18n
|
||||
//= require_tree ./admins
|
||||
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
|
||||
}
|
||||
});
|
||||
|
||||
// ******** select2 global config ********
|
||||
$.fn.select2.defaults.set('theme', 'bootstrap4');
|
||||
$.fn.select2.defaults.set('language', 'zh-CN');
|
||||
|
||||
Turbolinks.setProgressBarDelay(200);
|
||||
|
||||
$.notifyDefaults({
|
||||
type: 'success',
|
||||
z_index: 9999,
|
||||
delay: 2000
|
||||
});
|
||||
|
||||
function show_success_flash(){
|
||||
$.notify({
|
||||
message: '操作成功'
|
||||
},{
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('turbolinks:load', function(){
|
||||
$('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' });
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
// 图片查看大图
|
||||
$('img.preview-image').bootstrapViewer();
|
||||
|
||||
// flash alert提示框自动关闭
|
||||
if($('.admin-alert-container .alert').length > 0){
|
||||
setTimeout(function(){
|
||||
$('.admin-alert-container .alert:not(.alert-danger)').alert('close');
|
||||
}, 2000);
|
||||
setTimeout(function(){
|
||||
$('.admin-alert-container .alert.alert-danger').alert('close');
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("turbolinks:before-cache", function () {
|
||||
$('[data-toggle="tooltip"]').tooltip('hide');
|
||||
$('[data-toggle="popover"]').popover('hide');
|
||||
});
|
||||
// var progressBar = new Turbolinks.ProgressBar();
|
||||
|
||||
// $(document).on('ajax:send', function(event){
|
||||
// console.log('ajax send', event);
|
||||
// progressBar.setValue(0)
|
||||
// progressBar.show()
|
||||
// });
|
||||
//
|
||||
// $(document).on('ajax:complete', function(event){
|
||||
// console.log('ajax complete', event);
|
||||
// progressBar.setValue(1)
|
||||
// progressBar.hide() // 分页时不触发,奇怪
|
||||
// });
|
||||
// $(document).on('ajax:success', function(event){
|
||||
// console.log('ajax success', event);
|
||||
// });
|
||||
// $(document).on('ajax:error', function(event){
|
||||
// console.log('ajax error', event);
|
||||
// });
|
||||
|
||||
$(function () {
|
||||
});
|
||||
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
$('.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 {
|
||||
}
|
||||
});
|
||||
|
||||
$('.attachment-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 {
|
||||
}
|
||||
});
|
||||
//= require rails-ujs
|
||||
//= require activestorage
|
||||
//= require turbolinks
|
||||
//= require jquery3
|
||||
//= require popper
|
||||
//= require bootstrap-sprockets
|
||||
//= require jquery.validate.min
|
||||
//= require additional-methods.min
|
||||
//= require bootstrap-notify
|
||||
//= require jquery.cookie.min
|
||||
//= require select2
|
||||
//= require moment.min
|
||||
//= require jquery.cxselect
|
||||
//= require bootstrap-datepicker
|
||||
//= require bootstrap-datetimepicker
|
||||
//= require bootstrap.viewer
|
||||
//= require bootstrap/bootstrap-toggle
|
||||
//= require jquery.mloading
|
||||
//= require jquery-confirm.min
|
||||
//= require common
|
||||
|
||||
//= require echarts
|
||||
//= require codemirror/lib/codemirror
|
||||
//= require codemirror/mode/shell/shell
|
||||
//= require editormd/editormd
|
||||
//= require editormd/languages/zh-tw
|
||||
//= require dragula/dragula
|
||||
|
||||
//= require_tree ./i18n
|
||||
//= require_tree ./admins
|
||||
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
|
||||
}
|
||||
});
|
||||
|
||||
// ******** select2 global config ********
|
||||
$.fn.select2.defaults.set('theme', 'bootstrap4');
|
||||
$.fn.select2.defaults.set('language', 'zh-CN');
|
||||
|
||||
Turbolinks.setProgressBarDelay(200);
|
||||
|
||||
$.notifyDefaults({
|
||||
type: 'success',
|
||||
z_index: 9999,
|
||||
delay: 2000
|
||||
});
|
||||
|
||||
function show_success_flash(){
|
||||
$.notify({
|
||||
message: '操作成功'
|
||||
},{
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('turbolinks:load', function(){
|
||||
$('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' });
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
// 图片查看大图
|
||||
$('img.preview-image').bootstrapViewer();
|
||||
|
||||
// flash alert提示框自动关闭
|
||||
if($('.admin-alert-container .alert').length > 0){
|
||||
setTimeout(function(){
|
||||
$('.admin-alert-container .alert:not(.alert-danger)').alert('close');
|
||||
}, 2000);
|
||||
setTimeout(function(){
|
||||
$('.admin-alert-container .alert.alert-danger').alert('close');
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("turbolinks:before-cache", function () {
|
||||
$('[data-toggle="tooltip"]').tooltip('hide');
|
||||
$('[data-toggle="popover"]').popover('hide');
|
||||
});
|
||||
// var progressBar = new Turbolinks.ProgressBar();
|
||||
|
||||
// $(document).on('ajax:send', function(event){
|
||||
// console.log('ajax send', event);
|
||||
// progressBar.setValue(0)
|
||||
// progressBar.show()
|
||||
// });
|
||||
//
|
||||
// $(document).on('ajax:complete', function(event){
|
||||
// console.log('ajax complete', event);
|
||||
// progressBar.setValue(1)
|
||||
// progressBar.hide() // 分页时不触发,奇怪
|
||||
// });
|
||||
// $(document).on('ajax:success', function(event){
|
||||
// console.log('ajax success', event);
|
||||
// });
|
||||
// $(document).on('ajax:error', function(event){
|
||||
// console.log('ajax error', event);
|
||||
// });
|
||||
|
||||
$(function () {
|
||||
});
|
||||
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
$('.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 {
|
||||
}
|
||||
});
|
||||
|
||||
$('.attachment-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 {
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// TOGGLE PUBLIC CLASS DEFINITION
|
||||
// ==============================
|
||||
|
||||
var Toggle = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = $.extend({}, this.defaults(), options)
|
||||
this.render()
|
||||
}
|
||||
|
||||
Toggle.VERSION = '2.2.0'
|
||||
|
||||
Toggle.DEFAULTS = {
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
onstyle: 'primary',
|
||||
offstyle: 'default',
|
||||
size: 'normal',
|
||||
style: '',
|
||||
width: null,
|
||||
height: null
|
||||
}
|
||||
|
||||
Toggle.prototype.defaults = function() {
|
||||
return {
|
||||
on: this.$element.attr('data-on') || Toggle.DEFAULTS.on,
|
||||
off: this.$element.attr('data-off') || Toggle.DEFAULTS.off,
|
||||
onstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle,
|
||||
offstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle,
|
||||
size: this.$element.attr('data-size') || Toggle.DEFAULTS.size,
|
||||
style: this.$element.attr('data-style') || Toggle.DEFAULTS.style,
|
||||
width: this.$element.attr('data-width') || Toggle.DEFAULTS.width,
|
||||
height: this.$element.attr('data-height') || Toggle.DEFAULTS.height
|
||||
}
|
||||
}
|
||||
|
||||
Toggle.prototype.render = function () {
|
||||
this._onstyle = 'btn-' + this.options.onstyle
|
||||
this._offstyle = 'btn-' + this.options.offstyle
|
||||
var size = this.options.size === 'large' ? 'btn-lg'
|
||||
: this.options.size === 'small' ? 'btn-sm'
|
||||
: this.options.size === 'mini' ? 'btn-xs'
|
||||
: ''
|
||||
var $toggleOn = $('<label class="btn">').html(this.options.on)
|
||||
.addClass(this._onstyle + ' ' + size)
|
||||
var $toggleOff = $('<label class="btn">').html(this.options.off)
|
||||
.addClass(this._offstyle + ' ' + size + ' active')
|
||||
var $toggleHandle = $('<span class="toggle-handle btn btn-default">')
|
||||
.addClass(size)
|
||||
var $toggleGroup = $('<div class="toggle-group">')
|
||||
.append($toggleOn, $toggleOff, $toggleHandle)
|
||||
var $toggle = $('<div class="toggle btn" data-toggle="toggle">')
|
||||
.addClass( this.$element.prop('checked') ? this._onstyle : this._offstyle+' off' )
|
||||
.addClass(size).addClass(this.options.style)
|
||||
|
||||
this.$element.wrap($toggle)
|
||||
$.extend(this, {
|
||||
$toggle: this.$element.parent(),
|
||||
$toggleOn: $toggleOn,
|
||||
$toggleOff: $toggleOff,
|
||||
$toggleGroup: $toggleGroup
|
||||
})
|
||||
this.$toggle.append($toggleGroup)
|
||||
|
||||
var width = this.options.width || Math.max($toggleOn.outerWidth(), $toggleOff.outerWidth())+($toggleHandle.outerWidth()/2)
|
||||
var height = this.options.height || Math.max($toggleOn.outerHeight(), $toggleOff.outerHeight())
|
||||
$toggleOn.addClass('toggle-on')
|
||||
$toggleOff.addClass('toggle-off')
|
||||
this.$toggle.css({ width: width, height: height })
|
||||
if (this.options.height) {
|
||||
$toggleOn.css('line-height', $toggleOn.height() + 'px')
|
||||
$toggleOff.css('line-height', $toggleOff.height() + 'px')
|
||||
}
|
||||
this.update(true)
|
||||
this.trigger(true)
|
||||
}
|
||||
|
||||
Toggle.prototype.toggle = function () {
|
||||
if (this.$element.prop('checked')) this.off()
|
||||
else this.on()
|
||||
}
|
||||
|
||||
Toggle.prototype.on = function (silent) {
|
||||
if (this.$element.prop('disabled')) return false
|
||||
this.$toggle.removeClass(this._offstyle + ' off').addClass(this._onstyle)
|
||||
this.$element.prop('checked', true)
|
||||
if (!silent) this.trigger()
|
||||
}
|
||||
|
||||
Toggle.prototype.off = function (silent) {
|
||||
if (this.$element.prop('disabled')) return false
|
||||
this.$toggle.removeClass(this._onstyle).addClass(this._offstyle + ' off')
|
||||
this.$element.prop('checked', false)
|
||||
if (!silent) this.trigger()
|
||||
}
|
||||
|
||||
Toggle.prototype.enable = function () {
|
||||
this.$toggle.removeAttr('disabled')
|
||||
this.$element.prop('disabled', false)
|
||||
}
|
||||
|
||||
Toggle.prototype.disable = function () {
|
||||
this.$toggle.attr('disabled', 'disabled')
|
||||
this.$element.prop('disabled', true)
|
||||
}
|
||||
|
||||
Toggle.prototype.update = function (silent) {
|
||||
if (this.$element.prop('disabled')) this.disable()
|
||||
else this.enable()
|
||||
if (this.$element.prop('checked')) this.on(silent)
|
||||
else this.off(silent)
|
||||
}
|
||||
|
||||
Toggle.prototype.trigger = function (silent) {
|
||||
this.$element.off('change.bs.toggle')
|
||||
if (!silent) this.$element.change()
|
||||
this.$element.on('change.bs.toggle', $.proxy(function() {
|
||||
this.update()
|
||||
}, this))
|
||||
}
|
||||
|
||||
Toggle.prototype.destroy = function() {
|
||||
this.$element.off('change.bs.toggle')
|
||||
this.$toggleGroup.remove()
|
||||
this.$element.removeData('bs.toggle')
|
||||
this.$element.unwrap()
|
||||
}
|
||||
|
||||
// TOGGLE PLUGIN DEFINITION
|
||||
// ========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.toggle')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.toggle', (data = new Toggle(this, options)))
|
||||
if (typeof option == 'string' && data[option]) data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.bootstrapToggle
|
||||
|
||||
$.fn.bootstrapToggle = Plugin
|
||||
$.fn.bootstrapToggle.Constructor = Toggle
|
||||
|
||||
// TOGGLE NO CONFLICT
|
||||
// ==================
|
||||
|
||||
$.fn.toggle.noConflict = function () {
|
||||
$.fn.bootstrapToggle = old
|
||||
return this
|
||||
}
|
||||
|
||||
// TOGGLE DATA-API
|
||||
// ===============
|
||||
|
||||
$(function() {
|
||||
$('input[type=checkbox][data-toggle^=toggle]').bootstrapToggle()
|
||||
})
|
||||
|
||||
$(document).on('click.bs.toggle', 'div[data-toggle^=toggle]', function(e) {
|
||||
var $checkbox = $(this).find('input[type=checkbox]')
|
||||
$checkbox.bootstrapToggle('toggle')
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
|
||||
//# sourceMappingURL=bootstrap-toggle.min.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"bootstrap-toggle.min.js","sources":["bootstrap-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","outerHeight","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":";;;;;;;CASE,SAAUA,GACV,YAoID,SAASC,GAAOC,GACf,MAAOC,MAAKC,KAAK,WAChB,GAAIC,GAAUL,EAAEG,MACZG,EAAUD,EAAMC,KAAK,aACrBC,EAA2B,gBAAVL,IAAsBA,CAEtCI,IAAMD,EAAMC,KAAK,YAAcA,EAAO,GAAIE,GAAOL,KAAMI,IACvC,gBAAVL,IAAsBI,EAAKJ,IAASI,EAAKJ,OAtItD,GAAIM,GAAS,SAAUC,EAASF,GAC/BJ,KAAKO,SAAYV,EAAES,GACnBN,KAAKI,QAAYP,EAAEW,UAAWR,KAAKS,WAAYL,GAC/CJ,KAAKU,SAGNL,GAAOM,QAAW,QAElBN,EAAOO,UACNC,GAAI,KACJC,IAAK,MACLC,QAAS,UACTC,SAAU,UACVC,KAAM,SACNC,MAAO,GACPC,MAAO,KACPC,OAAQ,MAGTf,EAAOgB,UAAUZ,SAAW,WAC3B,OACCI,GAAIb,KAAKO,SAASe,KAAK,YAAcjB,EAAOO,SAASC,GACrDC,IAAKd,KAAKO,SAASe,KAAK,aAAejB,EAAOO,SAASE,IACvDC,QAASf,KAAKO,SAASe,KAAK,iBAAmBjB,EAAOO,SAASG,QAC/DC,SAAUhB,KAAKO,SAASe,KAAK,kBAAoBjB,EAAOO,SAASI,SACjEC,KAAMjB,KAAKO,SAASe,KAAK,cAAgBjB,EAAOO,SAASK,KACzDC,MAAOlB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASM,MAC3DC,MAAOnB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASO,MAC3DC,OAAQpB,KAAKO,SAASe,KAAK,gBAAkBjB,EAAOO,SAASQ,SAI/Df,EAAOgB,UAAUX,OAAS,WACzBV,KAAKuB,SAAW,OAASvB,KAAKI,QAAQW,QACtCf,KAAKwB,UAAY,OAASxB,KAAKI,QAAQY,QACvC,IAAIC,GAA6B,UAAtBjB,KAAKI,QAAQa,KAAmB,SAClB,UAAtBjB,KAAKI,QAAQa,KAAmB,SACV,SAAtBjB,KAAKI,QAAQa,KAAkB,SAC/B,GACCQ,EAAY5B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQS,IACzDc,SAAS3B,KAAKuB,SAAW,IAAMN,GAC7BW,EAAa/B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQU,KAC1Da,SAAS3B,KAAKwB,UAAY,IAAMP,EAAO,WACrCY,EAAgBhC,EAAE,gDACpB8B,SAASV,GACPa,EAAejC,EAAE,8BACnBkC,OAAON,EAAWG,EAAYC,GAC5BG,EAAUnC,EAAE,iDACd8B,SAAU3B,KAAKO,SAAS0B,KAAK,WAAajC,KAAKuB,SAAWvB,KAAKwB,UAAU,QACzEG,SAASV,GAAMU,SAAS3B,KAAKI,QAAQc,MAEvClB,MAAKO,SAAS2B,KAAKF,GACnBnC,EAAEW,OAAOR,MACRgC,QAAShC,KAAKO,SAAS4B,SACvBV,UAAWA,EACXG,WAAYA,EACZE,aAAcA,IAEf9B,KAAKgC,QAAQD,OAAOD,EAEpB,IAAIX,GAAQnB,KAAKI,QAAQe,OAASiB,KAAKC,IAAIZ,EAAUa,aAAcV,EAAWU,cAAeT,EAAcS,aAAa,EACpHlB,EAASpB,KAAKI,QAAQgB,QAAUgB,KAAKC,IAAIZ,EAAUc,cAAeX,EAAWW,cACjFd,GAAUE,SAAS,aACnBC,EAAWD,SAAS,cACpB3B,KAAKgC,QAAQQ,KAAMrB,MAAOA,EAAOC,OAAQA,IACrCpB,KAAKI,QAAQgB,SAChBK,EAAUe,IAAI,cAAef,EAAUL,SAAW,MAClDQ,EAAWY,IAAI,cAAeZ,EAAWR,SAAW,OAErDpB,KAAKyC,QAAO,GACZzC,KAAK0C,SAAQ,IAGdrC,EAAOgB,UAAUsB,OAAS,WACrB3C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKc,MACnCd,KAAKa,MAGXR,EAAOgB,UAAUR,GAAK,SAAU+B,GAC/B,MAAI5C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQa,YAAY7C,KAAKwB,UAAY,QAAQG,SAAS3B,KAAKuB,UAChEvB,KAAKO,SAAS0B,KAAK,WAAW,QACzBW,GAAQ5C,KAAK0C,aAGnBrC,EAAOgB,UAAUP,IAAM,SAAU8B,GAChC,MAAI5C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQa,YAAY7C,KAAKuB,UAAUI,SAAS3B,KAAKwB,UAAY,QAClExB,KAAKO,SAAS0B,KAAK,WAAW,QACzBW,GAAQ5C,KAAK0C,aAGnBrC,EAAOgB,UAAUyB,OAAS,WACzB9C,KAAKgC,QAAQe,WAAW,YACxB/C,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAU2B,QAAU,WAC1BhD,KAAKgC,QAAQV,KAAK,WAAY,YAC9BtB,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAUoB,OAAS,SAAUG,GAC/B5C,KAAKO,SAAS0B,KAAK,YAAajC,KAAKgD,UACpChD,KAAK8C,SACN9C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKa,GAAG+B,GACtC5C,KAAKc,IAAI8B,IAGfvC,EAAOgB,UAAUqB,QAAU,SAAUE,GACpC5C,KAAKO,SAASO,IAAI,oBACb8B,GAAQ5C,KAAKO,SAAS0C,SAC3BjD,KAAKO,SAASM,GAAG,mBAAoBhB,EAAEqD,MAAM,WAC5ClD,KAAKyC,UACHzC,QAGJK,EAAOgB,UAAU8B,QAAU,WAC1BnD,KAAKO,SAASO,IAAI,oBAClBd,KAAK8B,aAAasB,SAClBpD,KAAKO,SAAS8C,WAAW,aACzBrD,KAAKO,SAAS+C,SAiBf,IAAIC,GAAM1D,EAAE2D,GAAGC,eAEf5D,GAAE2D,GAAGC,gBAA8B3D,EACnCD,EAAE2D,GAAGC,gBAAgBC,YAAcrD,EAKnCR,EAAE2D,GAAGb,OAAOgB,WAAa,WAExB,MADA9D,GAAE2D,GAAGC,gBAAkBF,EAChBvD,MAMRH,EAAE,WACDA,EAAE,6CAA6C4D,oBAGhD5D,EAAE+D,UAAU/C,GAAG,kBAAmB,2BAA4B,SAASgD,GACtE,GAAIC,GAAYjE,EAAEG,MAAM+D,KAAK,uBAC7BD,GAAUL,gBAAgB,UAC1BI,EAAEG,oBAGFC"}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap2-toggle.js v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// TOGGLE PUBLIC CLASS DEFINITION
|
||||
// ==============================
|
||||
|
||||
var Toggle = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = $.extend({}, this.defaults(), options)
|
||||
this.render()
|
||||
}
|
||||
|
||||
Toggle.VERSION = '2.2.0'
|
||||
|
||||
Toggle.DEFAULTS = {
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
onstyle: 'primary',
|
||||
offstyle: 'default',
|
||||
size: 'normal',
|
||||
style: '',
|
||||
width: null,
|
||||
height: null
|
||||
}
|
||||
|
||||
Toggle.prototype.defaults = function() {
|
||||
return {
|
||||
on: this.$element.attr('data-on') || Toggle.DEFAULTS.on,
|
||||
off: this.$element.attr('data-off') || Toggle.DEFAULTS.off,
|
||||
onstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle,
|
||||
offstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle,
|
||||
size: this.$element.attr('data-size') || Toggle.DEFAULTS.size,
|
||||
style: this.$element.attr('data-style') || Toggle.DEFAULTS.style,
|
||||
width: this.$element.attr('data-width') || Toggle.DEFAULTS.width,
|
||||
height: this.$element.attr('data-height') || Toggle.DEFAULTS.height
|
||||
}
|
||||
}
|
||||
|
||||
Toggle.prototype.render = function () {
|
||||
this._onstyle = 'btn-' + this.options.onstyle
|
||||
this._offstyle = 'btn-' + this.options.offstyle
|
||||
var size = this.options.size === 'large' ? 'btn-large'
|
||||
: this.options.size === 'small' ? 'btn-small'
|
||||
: this.options.size === 'mini' ? 'btn-mini'
|
||||
: ''
|
||||
var $toggleOn = $('<label class="btn">').html(this.options.on)
|
||||
.addClass(this._onstyle + ' ' + size)
|
||||
var $toggleOff = $('<label class="btn">').html(this.options.off)
|
||||
.addClass(this._offstyle + ' ' + size + ' active')
|
||||
var $toggleHandle = $('<span class="toggle-handle btn btn-default">')
|
||||
.addClass(size)
|
||||
var $toggleGroup = $('<div class="toggle-group">')
|
||||
.append($toggleOn, $toggleOff, $toggleHandle)
|
||||
var $toggle = $('<div class="toggle btn" data-toggle="toggle">')
|
||||
.addClass( this.$element.prop('checked') ? this._onstyle : this._offstyle+' off' )
|
||||
.addClass(size).addClass(this.options.style)
|
||||
|
||||
this.$element.wrap($toggle)
|
||||
$.extend(this, {
|
||||
$toggle: this.$element.parent(),
|
||||
$toggleOn: $toggleOn,
|
||||
$toggleOff: $toggleOff,
|
||||
$toggleGroup: $toggleGroup
|
||||
})
|
||||
this.$toggle.append($toggleGroup)
|
||||
|
||||
var width = this.options.width || Math.max($toggleOn.width(), $toggleOff.width())+($toggleHandle.outerWidth()/2)
|
||||
var height = this.options.height || Math.max($toggleOn.height(), $toggleOff.height())
|
||||
$toggleOn.addClass('toggle-on')
|
||||
$toggleOff.addClass('toggle-off')
|
||||
this.$toggle.css({ width: width, height: height })
|
||||
if (this.options.height) {
|
||||
$toggleOn.css('line-height', $toggleOn.height() + 'px')
|
||||
$toggleOff.css('line-height', $toggleOff.height() + 'px')
|
||||
}
|
||||
this.update(true)
|
||||
this.trigger(true)
|
||||
}
|
||||
|
||||
Toggle.prototype.toggle = function () {
|
||||
if (this.$element.prop('checked')) this.off()
|
||||
else this.on()
|
||||
}
|
||||
|
||||
Toggle.prototype.on = function (silent) {
|
||||
if (this.$element.prop('disabled')) return false
|
||||
this.$toggle.removeClass(this._offstyle + ' off').addClass(this._onstyle)
|
||||
this.$element.prop('checked', true)
|
||||
if (!silent) this.trigger()
|
||||
}
|
||||
|
||||
Toggle.prototype.off = function (silent) {
|
||||
if (this.$element.prop('disabled')) return false
|
||||
this.$toggle.removeClass(this._onstyle).addClass(this._offstyle + ' off')
|
||||
this.$element.prop('checked', false)
|
||||
if (!silent) this.trigger()
|
||||
}
|
||||
|
||||
Toggle.prototype.enable = function () {
|
||||
this.$toggle.removeAttr('disabled')
|
||||
this.$element.prop('disabled', false)
|
||||
}
|
||||
|
||||
Toggle.prototype.disable = function () {
|
||||
this.$toggle.attr('disabled', 'disabled')
|
||||
this.$element.prop('disabled', true)
|
||||
}
|
||||
|
||||
Toggle.prototype.update = function (silent) {
|
||||
if (this.$element.prop('disabled')) this.disable()
|
||||
else this.enable()
|
||||
if (this.$element.prop('checked')) this.on(silent)
|
||||
else this.off(silent)
|
||||
}
|
||||
|
||||
Toggle.prototype.trigger = function (silent) {
|
||||
this.$element.off('change.bs.toggle')
|
||||
if (!silent) this.$element.change()
|
||||
this.$element.on('change.bs.toggle', $.proxy(function() {
|
||||
this.update()
|
||||
}, this))
|
||||
}
|
||||
|
||||
Toggle.prototype.destroy = function() {
|
||||
this.$element.off('change.bs.toggle')
|
||||
this.$toggleGroup.remove()
|
||||
this.$element.removeData('bs.toggle')
|
||||
this.$element.unwrap()
|
||||
}
|
||||
|
||||
// TOGGLE PLUGIN DEFINITION
|
||||
// ========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.toggle')
|
||||
var options = typeof option == 'object' && option
|
||||
|
||||
if (!data) $this.data('bs.toggle', (data = new Toggle(this, options)))
|
||||
if (typeof option == 'string' && data[option]) data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.bootstrapToggle
|
||||
|
||||
$.fn.bootstrapToggle = Plugin
|
||||
$.fn.bootstrapToggle.Constructor = Toggle
|
||||
|
||||
// TOGGLE NO CONFLICT
|
||||
// ==================
|
||||
|
||||
$.fn.toggle.noConflict = function () {
|
||||
$.fn.bootstrapToggle = old
|
||||
return this
|
||||
}
|
||||
|
||||
// TOGGLE DATA-API
|
||||
// ===============
|
||||
|
||||
$(function() {
|
||||
$('input[type=checkbox][data-toggle^=toggle]').bootstrapToggle()
|
||||
})
|
||||
|
||||
$(document).on('click.bs.toggle', 'div[data-toggle^=toggle]', function(e) {
|
||||
var $checkbox = $(this).find('input[type=checkbox]')
|
||||
$checkbox.bootstrapToggle('toggle')
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap2-toggle.js v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-large":"small"===this.options.size?"btn-small":"mini"===this.options.size?"btn-mini":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.width(),d.width())+e.outerWidth()/2,i=this.options.height||Math.max(c.height(),d.height());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
|
||||
//# sourceMappingURL=bootstrap2-toggle.min.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"bootstrap2-toggle.min.js","sources":["bootstrap2-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":";;;;;;;CASE,SAAUA,GACV,YAoID,SAASC,GAAOC,GACf,MAAOC,MAAKC,KAAK,WAChB,GAAIC,GAAUL,EAAEG,MACZG,EAAUD,EAAMC,KAAK,aACrBC,EAA2B,gBAAVL,IAAsBA,CAEtCI,IAAMD,EAAMC,KAAK,YAAcA,EAAO,GAAIE,GAAOL,KAAMI,IACvC,gBAAVL,IAAsBI,EAAKJ,IAASI,EAAKJ,OAtItD,GAAIM,GAAS,SAAUC,EAASF,GAC/BJ,KAAKO,SAAYV,EAAES,GACnBN,KAAKI,QAAYP,EAAEW,UAAWR,KAAKS,WAAYL,GAC/CJ,KAAKU,SAGNL,GAAOM,QAAW,QAElBN,EAAOO,UACNC,GAAI,KACJC,IAAK,MACLC,QAAS,UACTC,SAAU,UACVC,KAAM,SACNC,MAAO,GACPC,MAAO,KACPC,OAAQ,MAGTf,EAAOgB,UAAUZ,SAAW,WAC3B,OACCI,GAAIb,KAAKO,SAASe,KAAK,YAAcjB,EAAOO,SAASC,GACrDC,IAAKd,KAAKO,SAASe,KAAK,aAAejB,EAAOO,SAASE,IACvDC,QAASf,KAAKO,SAASe,KAAK,iBAAmBjB,EAAOO,SAASG,QAC/DC,SAAUhB,KAAKO,SAASe,KAAK,kBAAoBjB,EAAOO,SAASI,SACjEC,KAAMjB,KAAKO,SAASe,KAAK,cAAgBjB,EAAOO,SAASK,KACzDC,MAAOlB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASM,MAC3DC,MAAOnB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASO,MAC3DC,OAAQpB,KAAKO,SAASe,KAAK,gBAAkBjB,EAAOO,SAASQ,SAI/Df,EAAOgB,UAAUX,OAAS,WACzBV,KAAKuB,SAAW,OAASvB,KAAKI,QAAQW,QACtCf,KAAKwB,UAAY,OAASxB,KAAKI,QAAQY,QACvC,IAAIC,GAA6B,UAAtBjB,KAAKI,QAAQa,KAAmB,YAClB,UAAtBjB,KAAKI,QAAQa,KAAmB,YACV,SAAtBjB,KAAKI,QAAQa,KAAkB,WAC/B,GACCQ,EAAY5B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQS,IACzDc,SAAS3B,KAAKuB,SAAW,IAAMN,GAC7BW,EAAa/B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQU,KAC1Da,SAAS3B,KAAKwB,UAAY,IAAMP,EAAO,WACrCY,EAAgBhC,EAAE,gDACpB8B,SAASV,GACPa,EAAejC,EAAE,8BACnBkC,OAAON,EAAWG,EAAYC,GAC5BG,EAAUnC,EAAE,iDACd8B,SAAU3B,KAAKO,SAAS0B,KAAK,WAAajC,KAAKuB,SAAWvB,KAAKwB,UAAU,QACzEG,SAASV,GAAMU,SAAS3B,KAAKI,QAAQc,MAEvClB,MAAKO,SAAS2B,KAAKF,GACnBnC,EAAEW,OAAOR,MACRgC,QAAShC,KAAKO,SAAS4B,SACvBV,UAAWA,EACXG,WAAYA,EACZE,aAAcA,IAEf9B,KAAKgC,QAAQD,OAAOD,EAEpB,IAAIX,GAAQnB,KAAKI,QAAQe,OAASiB,KAAKC,IAAIZ,EAAUN,QAASS,EAAWT,SAAUU,EAAcS,aAAa,EAC1GlB,EAASpB,KAAKI,QAAQgB,QAAUgB,KAAKC,IAAIZ,EAAUL,SAAUQ,EAAWR,SAC5EK,GAAUE,SAAS,aACnBC,EAAWD,SAAS,cACpB3B,KAAKgC,QAAQO,KAAMpB,MAAOA,EAAOC,OAAQA,IACrCpB,KAAKI,QAAQgB,SAChBK,EAAUc,IAAI,cAAed,EAAUL,SAAW,MAClDQ,EAAWW,IAAI,cAAeX,EAAWR,SAAW,OAErDpB,KAAKwC,QAAO,GACZxC,KAAKyC,SAAQ,IAGdpC,EAAOgB,UAAUqB,OAAS,WACrB1C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKc,MACnCd,KAAKa,MAGXR,EAAOgB,UAAUR,GAAK,SAAU8B,GAC/B,MAAI3C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQY,YAAY5C,KAAKwB,UAAY,QAAQG,SAAS3B,KAAKuB,UAChEvB,KAAKO,SAAS0B,KAAK,WAAW,QACzBU,GAAQ3C,KAAKyC,aAGnBpC,EAAOgB,UAAUP,IAAM,SAAU6B,GAChC,MAAI3C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQY,YAAY5C,KAAKuB,UAAUI,SAAS3B,KAAKwB,UAAY,QAClExB,KAAKO,SAAS0B,KAAK,WAAW,QACzBU,GAAQ3C,KAAKyC,aAGnBpC,EAAOgB,UAAUwB,OAAS,WACzB7C,KAAKgC,QAAQc,WAAW,YACxB9C,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAU0B,QAAU,WAC1B/C,KAAKgC,QAAQV,KAAK,WAAY,YAC9BtB,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAUmB,OAAS,SAAUG,GAC/B3C,KAAKO,SAAS0B,KAAK,YAAajC,KAAK+C,UACpC/C,KAAK6C,SACN7C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKa,GAAG8B,GACtC3C,KAAKc,IAAI6B,IAGftC,EAAOgB,UAAUoB,QAAU,SAAUE,GACpC3C,KAAKO,SAASO,IAAI,oBACb6B,GAAQ3C,KAAKO,SAASyC,SAC3BhD,KAAKO,SAASM,GAAG,mBAAoBhB,EAAEoD,MAAM,WAC5CjD,KAAKwC,UACHxC,QAGJK,EAAOgB,UAAU6B,QAAU,WAC1BlD,KAAKO,SAASO,IAAI,oBAClBd,KAAK8B,aAAaqB,SAClBnD,KAAKO,SAAS6C,WAAW,aACzBpD,KAAKO,SAAS8C,SAiBf,IAAIC,GAAMzD,EAAE0D,GAAGC,eAEf3D,GAAE0D,GAAGC,gBAA8B1D,EACnCD,EAAE0D,GAAGC,gBAAgBC,YAAcpD,EAKnCR,EAAE0D,GAAGb,OAAOgB,WAAa,WAExB,MADA7D,GAAE0D,GAAGC,gBAAkBF,EAChBtD,MAMRH,EAAE,WACDA,EAAE,6CAA6C2D,oBAGhD3D,EAAE8D,UAAU9C,GAAG,kBAAmB,2BAA4B,SAAS+C,GACtE,GAAIC,GAAYhE,EAAEG,MAAM8D,KAAK,uBAC7BD,GAAUL,gBAAgB,UAC1BI,EAAEG,oBAGFC"}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
@import "jquery.mloading";
|
||||
@import "jquery-confirm.min";
|
||||
@import "bootstrap-datetimepicker.min";
|
||||
@import "bootstrap/bootstrap-toggle.min";
|
||||
|
||||
@import "codemirror/lib/codemirror";
|
||||
@import "editormd/css/editormd.min";
|
||||
|
|
@ -203,4 +204,14 @@ input.form-control {
|
|||
color: #23272B;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
padding: 0.75rem 0.1rem;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.table .thead-light th{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.css v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
.checkbox label .toggle,
|
||||
.checkbox-inline .toggle {
|
||||
margin-left: -20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.toggle input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
.toggle-group {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transition: left 0.35s;
|
||||
-webkit-transition: left 0.35s;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.toggle.off .toggle-group {
|
||||
left: -100%;
|
||||
}
|
||||
.toggle-on {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 50%;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.toggle-off {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.toggle-handle {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
height: 100%;
|
||||
width: 0px;
|
||||
border-width: 0 1px;
|
||||
}
|
||||
|
||||
.toggle.btn { min-width: 59px; min-height: 34px; }
|
||||
.toggle-on.btn { padding-right: 24px; }
|
||||
.toggle-off.btn { padding-left: 24px; }
|
||||
|
||||
.toggle.btn-lg { min-width: 79px; min-height: 45px; }
|
||||
.toggle-on.btn-lg { padding-right: 31px; }
|
||||
.toggle-off.btn-lg { padding-left: 31px; }
|
||||
.toggle-handle.btn-lg { width: 40px; }
|
||||
|
||||
.toggle.btn-sm { min-width: 50px; min-height: 30px;}
|
||||
.toggle-on.btn-sm { padding-right: 20px; }
|
||||
.toggle-off.btn-sm { padding-left: 20px; }
|
||||
|
||||
.toggle.btn-xs { min-width: 35px; min-height: 22px;}
|
||||
.toggle-on.btn-xs { padding-right: 12px; }
|
||||
.toggle-off.btn-xs { padding-left: 12px; }
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap-toggle.css v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}
|
||||
.toggle{position:relative;overflow:hidden}
|
||||
.toggle input[type=checkbox]{display:none}
|
||||
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
|
||||
.toggle.off .toggle-group{left:-100%}
|
||||
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
|
||||
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
|
||||
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
|
||||
.toggle.btn{min-width:59px;min-height:34px}
|
||||
.toggle-on.btn{padding-right:24px}
|
||||
.toggle-off.btn{padding-left:24px}
|
||||
.toggle.btn-lg{min-width:79px;min-height:45px}
|
||||
.toggle-on.btn-lg{padding-right:31px}
|
||||
.toggle-off.btn-lg{padding-left:31px}
|
||||
.toggle-handle.btn-lg{width:40px}
|
||||
.toggle.btn-sm{min-width:50px;min-height:30px}
|
||||
.toggle-on.btn-sm{padding-right:20px}
|
||||
.toggle-off.btn-sm{padding-left:20px}
|
||||
.toggle.btn-xs{min-width:35px;min-height:22px}
|
||||
.toggle-on.btn-xs{padding-right:12px}
|
||||
.toggle-off.btn-xs{padding-left:12px}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap2-toggle.css v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
label.checkbox .toggle,
|
||||
label.checkbox.inline .toggle {
|
||||
margin-left: -20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.toggle {
|
||||
min-width: 40px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.toggle input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
.toggle-group {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transition: left 0.35s;
|
||||
-webkit-transition: left 0.35s;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.toggle.off .toggle-group {
|
||||
left: -100%;
|
||||
}
|
||||
.toggle-on {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 50%;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.toggle-off {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.toggle-handle {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
height: 100%;
|
||||
width: 0px;
|
||||
border-width: 0 1px;
|
||||
}
|
||||
.toggle-handle.btn-mini {
|
||||
top: -1px;
|
||||
}
|
||||
.toggle.btn { min-width: 30px; }
|
||||
.toggle-on.btn { padding-right: 24px; }
|
||||
.toggle-off.btn { padding-left: 24px; }
|
||||
|
||||
.toggle.btn-large { min-width: 40px; }
|
||||
.toggle-on.btn-large { padding-right: 35px; }
|
||||
.toggle-off.btn-large { padding-left: 35px; }
|
||||
|
||||
.toggle.btn-small { min-width: 25px; }
|
||||
.toggle-on.btn-small { padding-right: 20px; }
|
||||
.toggle-off.btn-small { padding-left: 20px; }
|
||||
|
||||
.toggle.btn-mini { min-width: 20px; }
|
||||
.toggle-on.btn-mini { padding-right: 12px; }
|
||||
.toggle-off.btn-mini { padding-left: 12px; }
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*! ========================================================================
|
||||
* Bootstrap Toggle: bootstrap2-toggle.css v2.2.0
|
||||
* http://www.bootstraptoggle.com
|
||||
* ========================================================================
|
||||
* Copyright 2014 Min Hur, The New York Times Company
|
||||
* Licensed under MIT
|
||||
* ======================================================================== */
|
||||
label.checkbox .toggle,label.checkbox.inline .toggle{margin-left:-20px;margin-right:5px}
|
||||
.toggle{min-width:40px;height:20px;position:relative;overflow:hidden}
|
||||
.toggle input[type=checkbox]{display:none}
|
||||
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
|
||||
.toggle.off .toggle-group{left:-100%}
|
||||
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
|
||||
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
|
||||
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
|
||||
.toggle-handle.btn-mini{top:-1px}
|
||||
.toggle.btn{min-width:30px}
|
||||
.toggle-on.btn{padding-right:24px}
|
||||
.toggle-off.btn{padding-left:24px}
|
||||
.toggle.btn-large{min-width:40px}
|
||||
.toggle-on.btn-large{padding-right:35px}
|
||||
.toggle-off.btn-large{padding-left:35px}
|
||||
.toggle.btn-small{min-width:25px}
|
||||
.toggle-on.btn-small{padding-right:20px}
|
||||
.toggle-off.btn-small{padding-left:20px}
|
||||
.toggle.btn-mini{min-width:20px}
|
||||
.toggle-on.btn-mini{padding-right:12px}
|
||||
.toggle-off.btn-mini{padding-left:12px}
|
||||
|
|
@ -124,7 +124,9 @@ class AccountsController < ApplicationController
|
|||
|
||||
sync_params = {
|
||||
password: params[:password].to_s,
|
||||
email: @user.mail
|
||||
email: @user.mail,
|
||||
login_name: @user.login,
|
||||
source_id: 0
|
||||
}
|
||||
|
||||
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,33 @@
|
|||
class Admins::DashboardsController < Admins::BaseController
|
||||
def index
|
||||
@active_user_count = User.where(last_login_on: today).count
|
||||
@weekly_active_user_count = User.where(last_login_on: current_week).count
|
||||
@month_active_user_count = User.where(last_login_on: current_month).count
|
||||
# 用户活跃数
|
||||
day_user_ids = CommitLog.where(created_at: today).pluck(:user_id).uniq
|
||||
weekly_user_ids = CommitLog.where(created_at: current_week).pluck(:user_id).uniq
|
||||
month_user_ids = CommitLog.where(created_at: current_month).pluck(:user_id).uniq
|
||||
@active_user_count = User.where(last_login_on: today).or(User.where(id: day_user_ids)).count
|
||||
@weekly_active_user_count = User.where(last_login_on: current_week).or(User.where(id: weekly_user_ids)).count
|
||||
@month_active_user_count = User.where(last_login_on: current_month).or(User.where(id: month_user_ids)).count
|
||||
user_ids = User.where(created_on: pre_week).pluck(:id).uniq
|
||||
weekly_keep_user_count = User.where(id: user_ids).where(last_login_on: current_week).count
|
||||
@weekly_keep_rate = format("%.2f", user_ids.size > 0 ? weekly_keep_user_count.to_f / user_ids.size : 0)
|
||||
|
||||
@new_user_count = User.where(created_on: current_month).count
|
||||
# 新用户注册数
|
||||
@day_new_user_count = User.where(created_on: today).count
|
||||
@weekly_new_user_count = User.where(created_on: current_week).count
|
||||
@month_new_user_count = User.where(created_on: current_month).count
|
||||
|
||||
# 活跃项目数
|
||||
day_project_ids = (CommitLog.where(created_at: today).pluck(:project_id).uniq + Issue.where(created_on: today).pluck(:project_id).uniq).uniq
|
||||
weekly_project_ids = (CommitLog.where(created_at: current_week).pluck(:project_id).uniq + Issue.where(created_on: current_week).pluck(:project_id).uniq).uniq
|
||||
month_project_ids = (CommitLog.where(created_at: current_month).pluck(:project_id).uniq + Issue.where(created_on: current_month).pluck(:project_id).uniq).uniq
|
||||
@day_active_project_count = Project.where(updated_on: today).or(Project.where(id: day_project_ids)).count
|
||||
@weekly_active_project_count = Project.where(updated_on: current_week).or(Project.where(id: weekly_project_ids)).count
|
||||
@month_active_project_count = Project.where(updated_on: current_month).or(Project.where(id: month_project_ids)).count
|
||||
|
||||
# 新增项目数
|
||||
@day_new_project_count = Project.where(created_on: today).count
|
||||
@weekly_new_project_count = Project.where(created_on: current_week).count
|
||||
@month_new_project_count = Project.where(created_on: current_month).count
|
||||
end
|
||||
|
||||
def month_active_user
|
||||
|
|
@ -42,10 +65,14 @@ class Admins::DashboardsController < Admins::BaseController
|
|||
end
|
||||
|
||||
def current_week
|
||||
7.days.ago.beginning_of_day..Time.now.end_of_day
|
||||
7.days.ago.end_of_day..Time.now.end_of_day
|
||||
end
|
||||
|
||||
def current_month
|
||||
30.days.ago.beginning_of_day..Time.now.end_of_day
|
||||
30.days.ago.end_of_day..Time.now.end_of_day
|
||||
end
|
||||
|
||||
def pre_week
|
||||
14.days.ago.end_of_day..7.days.ago.end_of_day
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
class Admins::FeedbacksController < Admins::BaseController
|
||||
before_action :get_feedback, only: [:new_history, :create_history, :destroy]
|
||||
|
||||
def index
|
||||
sort_by = Feedback.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
|
||||
sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
|
||||
feedbacks = Feedback.order("#{sort_by} #{sort_direction}")
|
||||
@feedbacks = paginate(feedbacks)
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @feedback.destroy
|
||||
redirect_to admins_feedbacks_path
|
||||
flash[:success] = "反馈意见删除成功"
|
||||
else
|
||||
redirect_to admins_feedbacks_path
|
||||
flash[:danger] = "反馈意见删除失败"
|
||||
end
|
||||
end
|
||||
|
||||
def new_history
|
||||
@feedback_message_history = FeedbackMessageHistory.new
|
||||
end
|
||||
|
||||
def create_history
|
||||
@feedback_message_history = @feedback.feedback_message_histories.new(feedback_message_history_params)
|
||||
@feedback_message_history.user = current_user
|
||||
if @feedback_message_history.save
|
||||
redirect_to admins_feedbacks_path
|
||||
flash[:success] = "发送通知成功"
|
||||
else
|
||||
redirect_to admins_feedbacks_path
|
||||
flash[:danger] = @feedback_message_history.errors.full_messages.join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def feedback_params
|
||||
params.require(:feedback).permit!
|
||||
end
|
||||
|
||||
def feedback_message_history_params
|
||||
params.require(:feedback_message_history).permit(:title, :content)
|
||||
end
|
||||
|
||||
def get_feedback
|
||||
@feedback = Feedback.find_by_id(params[:id])
|
||||
end
|
||||
end
|
||||
|
|
@ -2,8 +2,24 @@ class Admins::MessageTemplatesController < Admins::BaseController
|
|||
before_action :get_template, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
message_templates = MessageTemplate.group(:type).count.keys
|
||||
@message_templates = kaminari_array_paginate(message_templates)
|
||||
message_templates = MessageTemplate.ransack(sys_notice_or_email_or_email_title_cont: params[:search]).result
|
||||
@message_templates = kaminari_paginate(message_templates)
|
||||
end
|
||||
|
||||
def new
|
||||
@message_template = MessageTemplate.new
|
||||
end
|
||||
|
||||
def create
|
||||
@message_template = MessageTemplate::CustomTip.new(message_template_params)
|
||||
@message_template.type = "MessageTemplate::CustomTip"
|
||||
if @message_template.save!
|
||||
redirect_to admins_message_templates_path
|
||||
flash[:success] = "创建消息模板成功"
|
||||
else
|
||||
render :new
|
||||
flash[:danger] = "创建消息模板失败"
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
|
|
@ -31,7 +47,9 @@ class Admins::MessageTemplatesController < Admins::BaseController
|
|||
|
||||
private
|
||||
def message_template_params
|
||||
params.require(@message_template.type.split("::").join("_").underscore.to_sym).permit!
|
||||
# type = @message_template.present? ? @message_template.type : "MessageTemplate::CustomTip"
|
||||
# params.require(type.split("::").join("_").underscore.to_sym).permit!
|
||||
params.require(:message_template).permit!
|
||||
end
|
||||
|
||||
def get_template
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
class Admins::NpsController < Admins::BaseController
|
||||
def index
|
||||
@on_off_switch = EduSetting.get("nps-on-off-switch").to_s == 'true'
|
||||
@user_nps = UserNp.joins(:user).order(created_at: :desc)
|
||||
keyword = params[:keyword].to_s.strip.presence
|
||||
if keyword
|
||||
sql = 'CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword'
|
||||
@user_nps = @user_nps.where(sql, keyword: "%#{keyword}%")
|
||||
end
|
||||
@user_nps = @user_nps.where("action_type != 'close'") if params[:done_score].present?
|
||||
@min_score = @user_nps.where("action_type != 'close'").minimum("score")
|
||||
@max_score = @user_nps.where("action_type != 'close'").maximum("score")
|
||||
@score_total_count = UserNp.where("action_type !='close'").count
|
||||
@user_nps = paginate @user_nps.includes(:user)
|
||||
end
|
||||
|
||||
def switch_change
|
||||
edu_setting = EduSetting.find_by(name: "nps-on-off-switch")
|
||||
if edu_setting.blank?
|
||||
edu_setting = EduSetting.new(name: "nps-on-off-switch")
|
||||
end
|
||||
edu_setting.value = params[:switch].to_s
|
||||
edu_setting.save
|
||||
render_ok
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
class Admins::ProjectIgnoresController < Admins::BaseController
|
||||
before_action :set_ignore, only: [:edit,:update, :destroy,:show]
|
||||
before_action :validate_params, only: [:create, :update]
|
||||
# before_action :validate_params, only: [:create, :update]
|
||||
|
||||
def index
|
||||
sort_by = Ignore.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
|
||||
|
|
@ -31,12 +31,12 @@ class Admins::ProjectIgnoresController < Admins::BaseController
|
|||
# }
|
||||
@project_ignore = Ignore.new(ignore_params)
|
||||
|
||||
if @project_ignore.save!
|
||||
if @project_ignore.save
|
||||
redirect_to admins_project_ignores_path
|
||||
flash[:success] = "创建成功"
|
||||
else
|
||||
render :new
|
||||
flash[:danger] = "创建失败"
|
||||
redirect_to admins_project_ignores_path
|
||||
flash[:danger] = @project_ignore.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -58,8 +58,8 @@ class Admins::ProjectIgnoresController < Admins::BaseController
|
|||
redirect_to admins_project_ignores_path
|
||||
flash[:success] = "更新成功"
|
||||
else
|
||||
render :edit
|
||||
flash[:danger] = "更新失败"
|
||||
redirect_to admins_project_ignores_path
|
||||
flash[:danger] = @project_ignore.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -98,23 +98,23 @@ class Admins::ProjectIgnoresController < Admins::BaseController
|
|||
params.require(:ignore).permit(:name,:content)
|
||||
end
|
||||
|
||||
def validate_params
|
||||
name = params[:ignore][:name]
|
||||
if name.blank?
|
||||
flash[:danger] = "名称不允许为空"
|
||||
redirect_to admins_project_ignores_path
|
||||
elsif check_ignore_present?(name) && @project_ignore.blank?
|
||||
flash[:danger] = "创建失败:名称已存在"
|
||||
redirect_to admins_project_ignores_path
|
||||
end
|
||||
end
|
||||
# def validate_params
|
||||
# name = params[:ignore][:name]
|
||||
# if name.blank?
|
||||
# flash[:danger] = "名称不允许为空"
|
||||
# redirect_to admins_project_ignores_path
|
||||
# elsif check_ignore_present?(name) && @project_ignore.blank?
|
||||
# flash[:danger] = "创建失败:名称已存在"
|
||||
# redirect_to admins_project_ignores_path
|
||||
# end
|
||||
# end
|
||||
|
||||
def check_ignore_present?(name)
|
||||
return true if name.blank?
|
||||
name_downcase = name.downcase
|
||||
name_upcase = name.upcase
|
||||
name_first_big = name.capitalize
|
||||
Ignore.exists?(name: name_downcase) || Ignore.exists?(name: name_upcase) || Ignore.exists?(name: name_first_big)
|
||||
end
|
||||
# def check_ignore_present?(name)
|
||||
# return true if name.blank?
|
||||
# name_downcase = name.downcase
|
||||
# name_upcase = name.upcase
|
||||
# name_first_big = name.capitalize
|
||||
# Ignore.exists?(name: name_downcase) || Ignore.exists?(name: name_upcase) || Ignore.exists?(name: name_first_big)
|
||||
# end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,17 +27,18 @@ class Admins::ProjectLanguagesController < Admins::BaseController
|
|||
flash[:success] = '创建成功'
|
||||
else
|
||||
redirect_to admins_project_languages_path
|
||||
flash[:danger] = '创建失败'
|
||||
flash[:danger] = @project_language.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @project_language.update_attribute(:name, @name)
|
||||
@project_language.attributes = {name: @name}
|
||||
if @project_language.save
|
||||
redirect_to admins_project_languages_path
|
||||
flash[:success] = '更新成功'
|
||||
else
|
||||
redirect_to admins_project_languages_path
|
||||
flash[:success] = '更新失败'
|
||||
flash[:danger] = @project_language.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class Admins::ProjectLicensesController < Admins::BaseController
|
||||
before_action :set_license, only: [:edit,:update, :destroy,:show]
|
||||
before_action :validate_params, only: [:create, :update]
|
||||
# before_action :validate_params, only: [:create, :update]
|
||||
|
||||
def index
|
||||
sort_by = License.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
|
||||
|
|
@ -30,13 +30,12 @@ class Admins::ProjectLicensesController < Admins::BaseController
|
|||
# position: max_position
|
||||
# }
|
||||
@project_license = License.new(license_params)
|
||||
|
||||
if @project_license.save!
|
||||
if @project_license.save
|
||||
redirect_to admins_project_licenses_path
|
||||
flash[:success] = "创建成功"
|
||||
else
|
||||
render :new
|
||||
flash[:danger] = "创建失败"
|
||||
redirect_to admins_project_licenses_path
|
||||
flash[:danger] = @project_license.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -54,12 +53,13 @@ class Admins::ProjectLicensesController < Admins::BaseController
|
|||
# permissions: permissions.to_s,
|
||||
# limitations: limitations.to_s
|
||||
# }
|
||||
if @project_license.update_attributes(license_params)
|
||||
@project_license.attributes = license_params
|
||||
if @project_license.save
|
||||
redirect_to admins_project_licenses_path
|
||||
flash[:success] = "更新成功"
|
||||
else
|
||||
render :edit
|
||||
flash[:danger] = "更新失败"
|
||||
render admins_project_licenses_path
|
||||
flash[:danger] = @project_license.errors.full_messages.join(",")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -98,23 +98,23 @@ class Admins::ProjectLicensesController < Admins::BaseController
|
|||
params.require(:license).permit(:name,:content)
|
||||
end
|
||||
|
||||
def validate_params
|
||||
name = params[:license][:name]
|
||||
if name.blank?
|
||||
flash[:danger] = "名称不允许为空"
|
||||
redirect_to admins_project_licenses_path
|
||||
elsif check_license_present?(name) && @project_license.blank?
|
||||
flash[:danger] = "创建失败:名称已存在"
|
||||
redirect_to admins_project_licenses_path
|
||||
end
|
||||
end
|
||||
# def validate_params
|
||||
# name = params[:license][:name]
|
||||
# if name.blank?
|
||||
# flash[:danger] = "名称不允许为空"
|
||||
# redirect_to admins_project_licenses_path
|
||||
# elsif check_license_present?(name) && @project_license.blank?
|
||||
# flash[:danger] = "创建失败:名称已存在"
|
||||
# redirect_to admins_project_licenses_path
|
||||
# end
|
||||
# end
|
||||
|
||||
def check_license_present?(name)
|
||||
return true if name.blank?
|
||||
name_downcase = name.downcase
|
||||
name_upcase = name.upcase
|
||||
name_first_big = name.capitalize
|
||||
License.exists?(name: name_downcase) || License.exists?(name: name_upcase) || License.exists?(name: name_first_big)
|
||||
end
|
||||
# def check_license_present?(name)
|
||||
# return true if name.blank?
|
||||
# name_downcase = name.downcase
|
||||
# name_upcase = name.upcase
|
||||
# name_first_big = name.capitalize
|
||||
# License.exists?(name: name_downcase) || License.exists?(name: name_upcase) || License.exists?(name: name_first_big)
|
||||
# end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
class Api::V1::BaseController < ApplicationController
|
||||
|
||||
include Api::ProjectHelper
|
||||
include Api::UserHelper
|
||||
include Api::PullHelper
|
||||
|
||||
# before_action :doorkeeper_authorize!
|
||||
# skip_before_action :user_setup
|
||||
|
||||
protected
|
||||
# def current_user
|
||||
# #client方法对接,需要一直带着用户标识uid
|
||||
# Rails.logger.info doorkeeper_token
|
||||
# if doorkeeper_token && doorkeeper_token.resource_owner_id.blank?
|
||||
# # return User.anonymous if params[:uid].nil?
|
||||
# # tip_exception("2222")
|
||||
# # return render_error('缺少用户标识!') if params[:uid].nil?
|
||||
# User.current = User.find(params[:uid])
|
||||
# else
|
||||
# User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
|
||||
# end
|
||||
# end
|
||||
|
||||
def limit
|
||||
params.fetch(:limit, 15)
|
||||
end
|
||||
def page
|
||||
params.fetch(:page, 1)
|
||||
end
|
||||
|
||||
# 具有对仓库的管理权限
|
||||
def require_manager_above
|
||||
@project = load_project
|
||||
return render_forbidden if !current_user.admin? && !@project.manager?(current_user)
|
||||
end
|
||||
|
||||
# 具有对仓库的操作权限
|
||||
def require_operate_above
|
||||
@project = load_project
|
||||
return render_forbidden if !current_user.admin? && !@project.operator?(current_user)
|
||||
end
|
||||
|
||||
# 具有仓库的操作权限或者fork仓库的操作权限
|
||||
def require_operate_above_or_fork_project
|
||||
@project = load_project
|
||||
puts !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
|
||||
return render_forbidden if !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
|
||||
end
|
||||
|
||||
# 具有对仓库的访问权限
|
||||
def require_public_and_member_above
|
||||
@project = load_project
|
||||
return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
class Api::V1::Projects::BranchesController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above, only: [:all]
|
||||
|
||||
def all
|
||||
@result_object = Api::V1::Projects::Branches::AllListService.call(@project, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
before_action :require_operate_above, only: [:create]
|
||||
|
||||
def create
|
||||
@result_object = Api::V1::Projects::Branches::CreateService.call(@project, branch_params, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
private
|
||||
def branch_params
|
||||
params.require(:branch).permit(:new_branch_name, :old_branch_name)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
class Api::V1::Projects::CodeStatsController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above, only: [:index]
|
||||
|
||||
def index
|
||||
@result_object = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: params[:ref]}, current_user&.gitea_token)
|
||||
puts @result_object
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
class Api::V1::Projects::CommitsController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above, only: [:index, :diff]
|
||||
|
||||
def index
|
||||
@result_object = Api::V1::Projects::Commits::ListService.call(@project, {page: page, limit: limit, sha: params[:sha]}, current_user&.gitea_token)
|
||||
puts @result_object
|
||||
end
|
||||
|
||||
def diff
|
||||
@result_object = Api::V1::Projects::Commits::DiffService.call(@project, params[:sha], current_user&.gitea_token)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
class Api::V1::Projects::ContentsController < Api::V1::BaseController
|
||||
before_action :require_operate_above_or_fork_project, only: [:batch]
|
||||
|
||||
def batch
|
||||
@batch_content_params = batch_content_params
|
||||
# 处理下author和committer信息,如果没传则默认为当前用户信息
|
||||
@batch_content_params.merge!(author_email: current_user.mail, author_name: current_user.login) if batch_content_params[:author_email].blank? && batch_content_params[:author_name].blank?
|
||||
@batch_content_params.merge!(committer_email: current_user.mail, committer_name: current_user.login) if batch_content_params[:committer_email].blank? && batch_content_params[:committer_name].blank?
|
||||
|
||||
@result_object = Api::V1::Projects::Contents::BatchCreateService.call(@project, @batch_content_params, @project.owner.gitea_token)
|
||||
end
|
||||
|
||||
private
|
||||
def batch_content_params
|
||||
params.require(:content).permit(:author_email, :author_name, :author_timeunix, :branch, :committer_email, :committer_name, :committer_timeunix, :message, :new_branch, files: [ :action_type, :content, :encoding, :file_path])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
class Api::V1::Projects::GitController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above, only: [:trees, :blobs]
|
||||
|
||||
def trees
|
||||
@result_object = Api::V1::Projects::Git::TreesService.call(@project, params[:sha], {recursive: params[:recursive], page: page, limit: limit}, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def blobs
|
||||
@result_object = Api::V1::Projects::Git::BlobsService.call(@project, params[:sha], current_user&.gitea_token)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class Api::V1::Projects::Pulls::BaseController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above
|
||||
before_action :load_pull_request
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
class Api::V1::Projects::Pulls::JournalsController < Api::V1::Projects::Pulls::BaseController
|
||||
|
||||
def index
|
||||
@journals = Api::V1::Projects::Pulls::Journals::ListService.call(@project, @pull_request, params, current_user)
|
||||
@journals = @journals.limit(200)
|
||||
end
|
||||
|
||||
def create
|
||||
@journal = Api::V1::Projects::Pulls::Journals::CreateService.call(@project, @pull_request, create_params, current_user)
|
||||
end
|
||||
|
||||
before_action :find_journal, only: [:update, :destroy]
|
||||
|
||||
def update
|
||||
@journal = Api::V1::Projects::Pulls::Journals::UpdateService.call(@project, @pull_request, @journal, update_params, current_user)
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @journal.destroy
|
||||
render_ok
|
||||
else
|
||||
render_error("删除评论失败!")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def create_params
|
||||
params.permit(:parent_id, :line_code, :note, :commit_id, :path, :type, :review_id, :diff => {})
|
||||
end
|
||||
|
||||
def update_params
|
||||
params.permit(:note, :commit_id, :state)
|
||||
end
|
||||
|
||||
def find_journal
|
||||
@journal = @pull_request.journals.find_by_id(params[:id])
|
||||
return render_not_found unless @journal.present?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
class Api::V1::Projects::Pulls::PullsController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above
|
||||
|
||||
def index
|
||||
@pulls = Api::V1::Projects::Pulls::ListService.call(@project, query_params)
|
||||
@pulls = kaminari_paginate(@pulls)
|
||||
end
|
||||
|
||||
before_action :load_pull_request, only: [:show]
|
||||
|
||||
def show
|
||||
@result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token)
|
||||
@last_review = @pull_request.reviews.order(created_at: :desc).take
|
||||
end
|
||||
|
||||
private
|
||||
def query_params
|
||||
params.permit(:status, :keyword, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
class Api::V1::Projects::Pulls::ReviewsController < Api::V1::Projects::Pulls::BaseController
|
||||
|
||||
def index
|
||||
@reviews = @pull_request.reviews
|
||||
@reviews = @reviews.where(status: params[:status]) if params[:status].present?
|
||||
# @reviews = kaminari_paginate(@reviews)
|
||||
end
|
||||
|
||||
before_action :require_reviewer, only: [:create]
|
||||
|
||||
def create
|
||||
@review = Api::V1::Projects::Pulls::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
|
||||
end
|
||||
|
||||
private
|
||||
def require_reviewer
|
||||
return render_forbidden('您没有审查权限,请联系项目管理员') if !current_user.admin? && !@pull_request.reviewers.exists?(current_user.id) && !@project.manager?(current_user)
|
||||
end
|
||||
|
||||
def review_params
|
||||
params.require(:review).permit(:content, :commit_id, :status)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
class Api::V1::Projects::Pulls::VersionsController < Api::V1::Projects::Pulls::BaseController
|
||||
|
||||
def index
|
||||
@result_object = Api::V1::Projects::Pulls::Versions::ListService.call(@project, @pull_request, {page: page, limit: limit}, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def diff
|
||||
@result_object = Api::V1::Projects::Pulls::Versions::GetDiffService.call(@project, @pull_request, params[:id], {filepath: params[:filepath]}, current_user&.gitea_token)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
class Api::V1::Projects::WebhooksController < Api::V1::BaseController
|
||||
before_action :require_manager_above
|
||||
before_action :find_webhook, only: [:show, :update, :destroy, :tests, :hooktasks]
|
||||
|
||||
def index
|
||||
# @result_object = Api::V1::Projects::Webhooks::ListService.call(@project, current_user&.gitea_token)
|
||||
@webhooks = @project.webhooks
|
||||
@webhooks = @webhooks.where(type: params[:type]) if params[:type].present?
|
||||
@webhooks = kaminari_paginate(@webhooks)
|
||||
end
|
||||
|
||||
def create
|
||||
return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 49
|
||||
@result_object = Api::V1::Projects::Webhooks::CreateService.call(@project, create_webhook_params, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def show
|
||||
@result_object = Api::V1::Projects::Webhooks::GetService.call(@project, params[:id], current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def update
|
||||
@result_object = Api::V1::Projects::Webhooks::UpdateService.call(@project, params[:id], webhook_params, current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@result_object = Api::V1::Projects::Webhooks::DeleteService.call(@project, params[:id], current_user&.gitea_token)
|
||||
if @result_object
|
||||
return render_ok
|
||||
else
|
||||
return render_error('删除失败!')
|
||||
end
|
||||
end
|
||||
|
||||
def tests
|
||||
@result_object = Api::V1::Projects::Webhooks::TestsService.call(@project, params[:id], current_user&.gitea_token)
|
||||
if @result_object
|
||||
return render_ok
|
||||
else
|
||||
return render_error('推送失败!')
|
||||
end
|
||||
end
|
||||
|
||||
def hooktasks
|
||||
@hooktasks = @webhook.tasks.where(is_delivered: true).order("delivered desc")
|
||||
@hooktasks = kaminari_paginate(@hooktasks)
|
||||
end
|
||||
|
||||
private
|
||||
def create_webhook_params
|
||||
params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, :type, events: [])
|
||||
end
|
||||
|
||||
def webhook_params
|
||||
params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, events: [])
|
||||
end
|
||||
|
||||
def find_webhook
|
||||
@webhook = Gitea::Webhook.find_by_id(params[:id])
|
||||
return render_not_found unless @webhook.present?
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
class Api::V1::ProjectsController < Api::V1::BaseController
|
||||
before_action :require_public_and_member_above, only: [:show, :compare, :blame]
|
||||
|
||||
def index
|
||||
render_ok
|
||||
end
|
||||
|
||||
def show
|
||||
@result_object = Api::V1::Projects::GetService.call(@project, current_user.gitea_token)
|
||||
end
|
||||
|
||||
def compare
|
||||
@result_object = Api::V1::Projects::CompareService.call(@project, params[:from], params[:to], current_user&.gitea_token)
|
||||
end
|
||||
|
||||
def blame
|
||||
@result_object = Api::V1::Projects::BlameService.call(@project, params[:sha], params[:filepath], current_user&.gitea_token)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
class Api::V1::Users::FeedbacksController < Api::V1::BaseController
|
||||
|
||||
before_action :load_observe_user
|
||||
before_action :check_auth_for_observe_user
|
||||
|
||||
def create
|
||||
@result = Api::V1::Users::Feedbacks::CreateService.call(@observe_user, feedback_params)
|
||||
return render_error("反馈意见创建失败.") if @result.nil?
|
||||
return render_ok
|
||||
end
|
||||
|
||||
private
|
||||
def feedback_params
|
||||
params.permit(:content)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
class Api::V1::Users::ProjectsController < Api::V1::BaseController
|
||||
before_action :load_observe_user
|
||||
|
||||
def index
|
||||
@object_results = Api::V1::Users::Projects::ListService.call(@observe_user, query_params, current_user)
|
||||
@projects = kaminari_paginate(@object_results)
|
||||
end
|
||||
|
||||
private
|
||||
def query_params
|
||||
params.permit(:category, :is_public, :project_type, :sort_by, :sort_direction, :search)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
class Api::V1::UsersController < Api::V1::BaseController
|
||||
|
||||
before_action :load_observe_user
|
||||
before_action :check_auth_for_observe_user
|
||||
|
||||
def send_email_vefify_code
|
||||
code = %W(0 1 2 3 4 5 6 7 8 9)
|
||||
verification_code = code.sample(6).join
|
||||
mail = params[:email]
|
||||
code_type = params[:code_type]
|
||||
|
||||
sign = Digest::MD5.hexdigest("#{OPENKEY}#{mail}")
|
||||
Rails.logger.info sign
|
||||
|
||||
tip_exception(501, "请求不合理") if sign != params[:smscode]
|
||||
|
||||
# 60s内不能重复发送
|
||||
send_email_limit_cache_key = "send_email_60_second_limit:#{mail}"
|
||||
tip_exception(-2, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key)
|
||||
send_email_control = LimitForbidControl::SendEmailCode.new(mail)
|
||||
tip_exception(-2, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
|
||||
begin
|
||||
UserMailer.update_email(mail, verification_code).deliver_now
|
||||
|
||||
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
|
||||
send_email_control.increment!
|
||||
rescue Exception => e
|
||||
logger_error(e)
|
||||
tip_exception(-2,"邮件发送失败,请稍后重试")
|
||||
end
|
||||
ver_params = {code_type: code_type, code: verification_code, email: mail}
|
||||
last_code = VerificationCode.where(code_type: code_type, email: mail).last
|
||||
last_code.update_attributes!({created_at: Time.current - 10.minute}) if last_code.present?
|
||||
data = VerificationCode.new(ver_params)
|
||||
if data.save!
|
||||
render_ok
|
||||
else
|
||||
tip_exception(-1, "创建数据失败")
|
||||
end
|
||||
end
|
||||
|
||||
def check_password
|
||||
password = params[:password]
|
||||
return tip_exception(-5, "8~16位密码,支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD
|
||||
return tip_exception(-5, "密码错误") unless @observe_user.check_password?(password)
|
||||
render_ok
|
||||
end
|
||||
|
||||
def check_email
|
||||
mail = strip(params[:email])
|
||||
return tip_exception(-2, "邮件格式有误") unless mail =~ CustomRegexp::EMAIL
|
||||
|
||||
exist_owner = Owner.find_by(mail: mail)
|
||||
return tip_exception(-2, '邮箱已被使用') if exist_owner
|
||||
render_ok
|
||||
end
|
||||
|
||||
def check_email_verify_code
|
||||
code = strip(params[:code])
|
||||
mail = strip(params[:email])
|
||||
code_type = params[:code_type]
|
||||
|
||||
return tip_exception(-2, "邮件格式有误") unless mail =~ CustomRegexp::EMAIL
|
||||
|
||||
verifi_code = VerificationCode.where(email: mail, code: code, code_type: code_type).last
|
||||
return render_ok if code == "123123" && EduSetting.get("code_debug") # 万能验证码,用于测试 # TODO 万能验证码,用于测试
|
||||
|
||||
return tip_exception(-6, "验证码不正确") if verifi_code&.code != code
|
||||
return tip_exception(-6, "验证码已失效") if !verifi_code&.effective?
|
||||
render_ok
|
||||
end
|
||||
|
||||
def update_email
|
||||
@result_object = Api::V1::Users::UpdateEmailService.call(@observe_user, params, current_user.gitea_token)
|
||||
if @result_object
|
||||
return render_ok
|
||||
else
|
||||
return render_error('更改邮箱失败!')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -248,42 +248,57 @@ class ApplicationController < ActionController::Base
|
|||
#return if params[:controller] == "main"
|
||||
# Find the current user
|
||||
#Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}")
|
||||
User.current = find_current_user
|
||||
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
|
||||
|
||||
# 开放课程通过链接访问的用户
|
||||
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
|
||||
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
|
||||
|
||||
if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
|
||||
user = open_class_user
|
||||
if user
|
||||
start_user_session(user)
|
||||
set_autologin_cookie(user)
|
||||
if request.headers["Authorization"].present? && request.headers["Authorization"].start_with?('Bearer')
|
||||
tip_exception(401, "请登录后再操作!") unless valid_doorkeeper_token?
|
||||
if @doorkeeper_token.present?
|
||||
# client方法对接,需要一直带着用户标识uid
|
||||
if @doorkeeper_token.resource_owner_id.blank?
|
||||
tip_exception(-1, "缺少用户标识!") if params[:uid].nil?
|
||||
User.current = User.find(params[:uid])
|
||||
else
|
||||
User.current = User.find_by(id: @doorkeeper_token.resource_owner_id)
|
||||
end
|
||||
User.current = user
|
||||
end
|
||||
else
|
||||
User.current = find_current_user
|
||||
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
|
||||
|
||||
# 开放课程通过链接访问的用户
|
||||
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank?
|
||||
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}"
|
||||
|
||||
if Digest::MD5.hexdigest(content) == params[:chinaoocKey]
|
||||
user = open_class_user
|
||||
if user
|
||||
start_user_session(user)
|
||||
set_autologin_cookie(user)
|
||||
end
|
||||
User.current = user
|
||||
end
|
||||
end
|
||||
|
||||
if !User.current.logged? && Rails.env.development?
|
||||
user = User.find 1
|
||||
User.current = user
|
||||
start_user_session(user)
|
||||
end
|
||||
|
||||
|
||||
# 测试版前端需求
|
||||
# logger.info("subdomain:#{request.subdomain}")
|
||||
# if request.subdomain != "www"
|
||||
# if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
|
||||
# User.current = User.find 81403
|
||||
# elsif params[:debug] == 'student'
|
||||
# User.current = User.find 8686
|
||||
# elsif params[:debug] == 'admin'
|
||||
# logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
|
||||
# user = User.find 36480
|
||||
# User.current = user
|
||||
# cookies.signed[:user_id] = user.id
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
# if !User.current.logged? && Rails.env.development?
|
||||
# User.current = User.find 1
|
||||
# end
|
||||
|
||||
|
||||
# 测试版前端需求
|
||||
logger.info("subdomain:#{request.subdomain}")
|
||||
# if request.subdomain != "www"
|
||||
# if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
|
||||
# User.current = User.find 81403
|
||||
# elsif params[:debug] == 'student'
|
||||
# User.current = User.find 8686
|
||||
# elsif params[:debug] == 'admin'
|
||||
# logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
|
||||
# user = User.find 36480
|
||||
# User.current = user
|
||||
# cookies.signed[:user_id] = user.id
|
||||
# end
|
||||
# end
|
||||
# User.current = User.find 81403
|
||||
end
|
||||
|
||||
|
|
@ -686,7 +701,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
@project, @owner = Project.find_with_namespace(namespace, id)
|
||||
|
||||
if @project and current_user.can_read_project?(@project)
|
||||
if @project and (current_user.can_read_project?(@project) || controller_path == "projects/project_invite_links")
|
||||
logger.info "###########: has project and can read project"
|
||||
@project
|
||||
# elsif @project && current_user.is_a?(AnonymousUser)
|
||||
|
|
@ -694,9 +709,15 @@ class ApplicationController < ActionController::Base
|
|||
# @project = nil if !@project.is_public?
|
||||
# render_forbidden and return
|
||||
else
|
||||
logger.info "###########:project not found"
|
||||
@project = nil
|
||||
render_not_found and return
|
||||
if @project.present?
|
||||
logger.info "###########: has project and but can't read project"
|
||||
@project = nil
|
||||
render_forbidden and return
|
||||
else
|
||||
logger.info "###########:project not found"
|
||||
@project = nil
|
||||
render_not_found and return
|
||||
end
|
||||
end
|
||||
@project
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,261 +1,258 @@
|
|||
#coding=utf-8
|
||||
#
|
||||
# 文件上传
|
||||
class AttachmentsController < ApplicationController
|
||||
before_action :require_login, :check_auth, except: [:show, :preview_attachment, :get_file]
|
||||
before_action :find_file, only: %i[show destroy]
|
||||
before_action :attachment_candown, only: [:show]
|
||||
skip_before_action :check_sign, only: [:show, :create]
|
||||
|
||||
include ApplicationHelper
|
||||
|
||||
def show
|
||||
# 1. 优先跳到cdn
|
||||
# 2. 如果没有cdn,send_file
|
||||
if @file.cloud_url.present?
|
||||
update_downloads(@file)
|
||||
redirect_to @file.cloud_url and return
|
||||
end
|
||||
|
||||
type_attachment = params[:disposition] || "attachment"
|
||||
if type_attachment == "inline"
|
||||
send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf'
|
||||
elsif type_attachment == "MP4"
|
||||
send_file_with_range absolute_path(local_path(@file)), disposition: 'inline', type: "video/mp4", range: true
|
||||
else
|
||||
send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream')
|
||||
end
|
||||
update_downloads(@file)
|
||||
end
|
||||
|
||||
|
||||
def get_file
|
||||
normal_status(-1, "参数缺失") if params[:download_url].blank?
|
||||
url = URI.encode(params[:download_url].to_s.gsub("http:", "https:"))
|
||||
if url.starts_with?(base_url)
|
||||
domain = Gitea.gitea_config[:domain]
|
||||
api_url = Gitea.gitea_config[:base_url]
|
||||
url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?')
|
||||
request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
|
||||
response = Faraday.get(request_url)
|
||||
filename = url.to_s.split("/").pop()
|
||||
else
|
||||
response = Faraday.get(url)
|
||||
filename = params[:download_url].to_s.split("/").pop()
|
||||
end
|
||||
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
|
||||
end
|
||||
|
||||
def create
|
||||
# 1. 本地存储
|
||||
# 2. 上传到云
|
||||
begin
|
||||
upload_file = params["file"] || params["#{params[:file_param_name]}"]# 这里的file_param_name是为了方便其他插件名称
|
||||
uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}")
|
||||
raise "未上传文件" unless upload_file
|
||||
|
||||
folder = file_storage_directory
|
||||
raise "存储目录未定义" unless folder.present?
|
||||
|
||||
month_folder = current_month_folder
|
||||
save_path = File.join(folder, month_folder)
|
||||
|
||||
ext = file_ext(upload_file.original_filename)
|
||||
|
||||
local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext)
|
||||
|
||||
content_type = upload_file.content_type.presence || 'application/octet-stream'
|
||||
|
||||
# remote_path = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type)
|
||||
remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端
|
||||
|
||||
logger.info "local_path: #{local_path}"
|
||||
logger.info "remote_path: #{remote_path}"
|
||||
|
||||
|
||||
disk_filename = local_path[save_path.size + 1, local_path.size]
|
||||
#存数据库
|
||||
#
|
||||
@attachment = Attachment.where(disk_filename: disk_filename,
|
||||
author_id: current_user.id,
|
||||
cloud_url: remote_path).first
|
||||
if @attachment.blank?
|
||||
@attachment = Attachment.new
|
||||
@attachment.filename = upload_file.original_filename
|
||||
@attachment.disk_filename = local_path[save_path.size + 1, local_path.size]
|
||||
@attachment.filesize = upload_file.tempfile.size
|
||||
@attachment.content_type = content_type
|
||||
@attachment.digest = digest
|
||||
@attachment.author_id = current_user.id
|
||||
@attachment.disk_directory = month_folder
|
||||
@attachment.cloud_url = remote_path
|
||||
@attachment.save!
|
||||
else
|
||||
logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}"
|
||||
end
|
||||
|
||||
render_json
|
||||
rescue => e
|
||||
uid_logger_error(e.message)
|
||||
tip_exception(e.message)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
begin
|
||||
@file_path = absolute_path(local_path(@file))
|
||||
#return normal_status(403, "") unless @file.author == current_user
|
||||
@file.destroy!
|
||||
|
||||
delete_file(@file_path)
|
||||
normal_status("删除成功")
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
tip_exception(e.message)
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
|
||||
# 附件为视频时,点击播放
|
||||
def preview_attachment
|
||||
attachment = Attachment.find_by(id: params[:id])
|
||||
dir_path = "#{Rails.root}/public/preview"
|
||||
Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
|
||||
if params[:status] == "preview"
|
||||
if system("cp -r #{absolute_path(local_path(attachment))} #{dir_path}/")
|
||||
render json: {status: 1, url: "/preview/#{attachment.disk_filename}"}
|
||||
else
|
||||
normal_status(-1, "出现错误,请稍后重试")
|
||||
end
|
||||
else
|
||||
if system("rm -rf #{dir_path}/#{attachment.disk_filename}")
|
||||
normal_status(1, "操作成功")
|
||||
else
|
||||
normal_status(-1, "出现错误,请稍后重试")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def find_file
|
||||
@file =
|
||||
if params[:type] == 'history'
|
||||
AttachmentHistory.find params[:id]
|
||||
else
|
||||
Attachment.find params[:id]
|
||||
end
|
||||
end
|
||||
|
||||
def delete_file(file_path)
|
||||
File.delete(file_path) if File.exist?(file_path)
|
||||
end
|
||||
|
||||
def current_month_folder
|
||||
date = Time.now
|
||||
"#{date.year}/#{date.month.to_s.rjust(2, '0')}"
|
||||
end
|
||||
|
||||
def file_ext(file_name)
|
||||
ext = ''
|
||||
exts = file_name.split(".")
|
||||
if exts.size > 1
|
||||
ext = ".#{exts.last}"
|
||||
end
|
||||
ext
|
||||
end
|
||||
|
||||
def file_save_to_local(save_path, temp_file, ext)
|
||||
unless Dir.exists?(save_path)
|
||||
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
|
||||
end
|
||||
|
||||
digest = md5_file(temp_file)
|
||||
digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}"
|
||||
local_file_path = File.join(save_path, digest) + ext
|
||||
save_temp_file(temp_file, local_file_path)
|
||||
|
||||
[local_file_path, digest]
|
||||
end
|
||||
|
||||
def save_temp_file(temp_file, save_file_path)
|
||||
File.open(save_file_path, 'wb') do |f|
|
||||
temp_file.rewind
|
||||
while (buffer = temp_file.read(8192))
|
||||
f.write(buffer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def md5_file(temp_file)
|
||||
md5 = Digest::MD5.new
|
||||
temp_file.rewind
|
||||
while (buffer = temp_file.read(8192))
|
||||
md5.update(buffer)
|
||||
end
|
||||
md5.hexdigest
|
||||
end
|
||||
|
||||
def file_save_to_ucloud(path, file, content_type)
|
||||
ufile = Gitlink::Ufile.new(
|
||||
ucloud_public_key: edu_setting('public_key'),
|
||||
ucloud_private_key: edu_setting('private_key'),
|
||||
ucloud_public_read: true,
|
||||
ucloud_public_bucket: edu_setting('public_bucket'),
|
||||
ucloud_public_bucket_host: edu_setting('public_bucket_host'),
|
||||
ucloud_public_cdn_host: edu_setting('public_cdn_host'),
|
||||
)
|
||||
File.open(file) do |f|
|
||||
ufile.put(path, f, 'Content-Type' => content_type)
|
||||
end
|
||||
edu_setting('public_cdn_host') + "/" + path
|
||||
end
|
||||
|
||||
def attachment_candown
|
||||
unless current_user.admin? || current_user.business?
|
||||
candown = true
|
||||
unless params[:type] == 'history'
|
||||
if @file.container && current_user.logged?
|
||||
if @file.container.is_a?(Issue)
|
||||
course = @file.container.project
|
||||
candown = course.member?(current_user) || course.is_public
|
||||
elsif @file.container.is_a?(Journal)
|
||||
course = @file.container.issue.project
|
||||
candown = course.member?(current_user)
|
||||
else
|
||||
course = nil
|
||||
end
|
||||
tip_exception(403, "您没有权限进入") if course.present? && !candown
|
||||
tip_exception(403, "您没有权限进入") if @file.container.is_a?(ApplyUserAuthentication)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_file_with_range(path, options = {})
|
||||
logger.info("########request.headers: #{request.headers}")
|
||||
logger.info("########request.headers: #{File.exist?(path)}")
|
||||
|
||||
if File.exist?(path)
|
||||
size = File.size(path)
|
||||
logger.info("########request.headers: #{request.headers}")
|
||||
if !request.headers["Range"]
|
||||
status_code = 200 # 200 OK
|
||||
offset = 0
|
||||
length = File.size(path)
|
||||
else
|
||||
status_code = 206 # 206 Partial Content
|
||||
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
|
||||
offset = bytes.begin
|
||||
length = bytes.end - bytes.begin
|
||||
end
|
||||
response.header["Accept-Ranges"] = "bytes"
|
||||
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes
|
||||
response.header["status"] = status_code
|
||||
|
||||
send_data IO.binread(path, length, offset), options
|
||||
else
|
||||
raise ActionController::MissingFile, "Cannot read file #{path}."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
#coding=utf-8
|
||||
#
|
||||
# 文件上传
|
||||
class AttachmentsController < ApplicationController
|
||||
before_action :require_login, :check_auth, except: [:show, :preview_attachment, :get_file]
|
||||
before_action :find_file, only: %i[show destroy]
|
||||
before_action :attachment_candown, only: [:show]
|
||||
skip_before_action :check_sign, only: [:show, :create]
|
||||
|
||||
include ApplicationHelper
|
||||
|
||||
def show
|
||||
# 1. 优先跳到cdn
|
||||
# 2. 如果没有cdn,send_file
|
||||
if @file.cloud_url.present?
|
||||
update_downloads(@file)
|
||||
redirect_to @file.cloud_url and return
|
||||
end
|
||||
|
||||
type_attachment = params[:disposition] || "attachment"
|
||||
if type_attachment == "inline"
|
||||
send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf'
|
||||
elsif type_attachment == "MP4"
|
||||
send_file_with_range absolute_path(local_path(@file)), disposition: 'inline', type: "video/mp4", range: true
|
||||
else
|
||||
send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream')
|
||||
end
|
||||
update_downloads(@file)
|
||||
end
|
||||
|
||||
|
||||
def get_file
|
||||
normal_status(-1, "参数缺失") if params[:download_url].blank?
|
||||
url = base_url.starts_with?("https:") ? URI.encode(params[:download_url].to_s.gsub("http:", "https:")) : URI.encode(params[:download_url].to_s)
|
||||
if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
|
||||
domain = GiteaService.gitea_config[:domain]
|
||||
api_url = GiteaService.gitea_config[:base_url]
|
||||
url = ("/repos"+url.split(base_url + "/api")[1]).gsub('?filepath=', '/').gsub('&', '?')
|
||||
request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
|
||||
response = Faraday.get(request_url)
|
||||
filename = url.to_s.split("/").pop()
|
||||
else
|
||||
response = Faraday.get(url)
|
||||
filename = params[:download_url].to_s.split("/").pop()
|
||||
end
|
||||
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
|
||||
end
|
||||
|
||||
def create
|
||||
# 1. 本地存储
|
||||
# 2. 上传到云
|
||||
begin
|
||||
upload_file = params["file"] || params["#{params[:file_param_name]}"]# 这里的file_param_name是为了方便其他插件名称
|
||||
uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}")
|
||||
raise "未上传文件" unless upload_file
|
||||
|
||||
folder = file_storage_directory
|
||||
raise "存储目录未定义" unless folder.present?
|
||||
|
||||
month_folder = current_month_folder
|
||||
save_path = File.join(folder, month_folder)
|
||||
|
||||
ext = file_ext(upload_file.original_filename)
|
||||
|
||||
local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext)
|
||||
|
||||
content_type = upload_file.content_type.presence || 'application/octet-stream'
|
||||
|
||||
# remote_path = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type)
|
||||
remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端
|
||||
|
||||
logger.info "local_path: #{local_path}"
|
||||
logger.info "remote_path: #{remote_path}"
|
||||
|
||||
|
||||
disk_filename = local_path[save_path.size + 1, local_path.size]
|
||||
#存数据库
|
||||
#
|
||||
@attachment = Attachment.where(disk_filename: disk_filename,
|
||||
author_id: current_user.id,
|
||||
cloud_url: remote_path).first
|
||||
if @attachment.blank?
|
||||
@attachment = Attachment.new
|
||||
@attachment.filename = upload_file.original_filename
|
||||
@attachment.disk_filename = local_path[save_path.size + 1, local_path.size]
|
||||
@attachment.filesize = upload_file.tempfile.size
|
||||
@attachment.content_type = content_type
|
||||
@attachment.digest = digest
|
||||
@attachment.author_id = current_user.id
|
||||
@attachment.disk_directory = month_folder
|
||||
@attachment.cloud_url = remote_path
|
||||
@attachment.save!
|
||||
else
|
||||
logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}"
|
||||
end
|
||||
|
||||
render_json
|
||||
rescue => e
|
||||
uid_logger_error(e.message)
|
||||
tip_exception(e.message)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
begin
|
||||
@file_path = absolute_path(local_path(@file))
|
||||
#return normal_status(403, "") unless @file.author == current_user
|
||||
@file.destroy!
|
||||
|
||||
delete_file(@file_path)
|
||||
normal_status("删除成功")
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
tip_exception(e.message)
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
|
||||
# 附件为视频时,点击播放
|
||||
def preview_attachment
|
||||
attachment = Attachment.find_by(id: params[:id])
|
||||
dir_path = "#{Rails.root}/public/preview"
|
||||
Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
|
||||
if params[:status] == "preview"
|
||||
if system("cp -r #{absolute_path(local_path(attachment))} #{dir_path}/")
|
||||
render json: {status: 1, url: "/preview/#{attachment.disk_filename}"}
|
||||
else
|
||||
normal_status(-1, "出现错误,请稍后重试")
|
||||
end
|
||||
else
|
||||
if system("rm -rf #{dir_path}/#{attachment.disk_filename}")
|
||||
normal_status(1, "操作成功")
|
||||
else
|
||||
normal_status(-1, "出现错误,请稍后重试")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def find_file
|
||||
@file =
|
||||
if params[:type] == 'history'
|
||||
AttachmentHistory.find params[:id]
|
||||
else
|
||||
Attachment.find params[:id]
|
||||
end
|
||||
end
|
||||
|
||||
def delete_file(file_path)
|
||||
File.delete(file_path) if File.exist?(file_path)
|
||||
end
|
||||
|
||||
def current_month_folder
|
||||
date = Time.now
|
||||
"#{date.year}/#{date.month.to_s.rjust(2, '0')}"
|
||||
end
|
||||
|
||||
def file_ext(file_name)
|
||||
ext = ''
|
||||
exts = file_name.split(".")
|
||||
if exts.size > 1
|
||||
ext = ".#{exts.last}"
|
||||
end
|
||||
ext
|
||||
end
|
||||
|
||||
def file_save_to_local(save_path, temp_file, ext)
|
||||
unless Dir.exists?(save_path)
|
||||
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
|
||||
end
|
||||
|
||||
digest = md5_file(temp_file)
|
||||
digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}"
|
||||
local_file_path = File.join(save_path, digest) + ext
|
||||
save_temp_file(temp_file, local_file_path)
|
||||
|
||||
[local_file_path, digest]
|
||||
end
|
||||
|
||||
def save_temp_file(temp_file, save_file_path)
|
||||
File.open(save_file_path, 'wb') do |f|
|
||||
temp_file.rewind
|
||||
while (buffer = temp_file.read(8192))
|
||||
f.write(buffer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def md5_file(temp_file)
|
||||
md5 = Digest::MD5.new
|
||||
temp_file.rewind
|
||||
while (buffer = temp_file.read(8192))
|
||||
md5.update(buffer)
|
||||
end
|
||||
md5.hexdigest
|
||||
end
|
||||
|
||||
def file_save_to_ucloud(path, file, content_type)
|
||||
ufile = Gitlink::Ufile.new(
|
||||
ucloud_public_key: edu_setting('public_key'),
|
||||
ucloud_private_key: edu_setting('private_key'),
|
||||
ucloud_public_read: true,
|
||||
ucloud_public_bucket: edu_setting('public_bucket'),
|
||||
ucloud_public_bucket_host: edu_setting('public_bucket_host'),
|
||||
ucloud_public_cdn_host: edu_setting('public_cdn_host'),
|
||||
)
|
||||
File.open(file) do |f|
|
||||
ufile.put(path, f, 'Content-Type' => content_type)
|
||||
end
|
||||
edu_setting('public_cdn_host') + "/" + path
|
||||
end
|
||||
|
||||
def attachment_candown
|
||||
unless current_user.admin? || current_user.business?
|
||||
candown = true
|
||||
if @file.container
|
||||
if @file.container.is_a?(Issue)
|
||||
project = @file.container.project
|
||||
candown = project.is_public || (current_user.logged? && project.member?(current_user))
|
||||
elsif @file.container.is_a?(Journal)
|
||||
project = @file.container.issue.project
|
||||
candown = project.is_public || (current_user.logged? && project.member?(current_user))
|
||||
else
|
||||
project = nil
|
||||
end
|
||||
tip_exception(403, "您没有权限进入") if project.present? && !candown
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_file_with_range(path, options = {})
|
||||
logger.info("########request.headers: #{request.headers}")
|
||||
logger.info("########request.headers: #{File.exist?(path)}")
|
||||
|
||||
if File.exist?(path)
|
||||
size = File.size(path)
|
||||
logger.info("########request.headers: #{request.headers}")
|
||||
if !request.headers["Range"]
|
||||
status_code = 200 # 200 OK
|
||||
offset = 0
|
||||
length = File.size(path)
|
||||
else
|
||||
status_code = 206 # 206 Partial Content
|
||||
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
|
||||
offset = bytes.begin
|
||||
length = bytes.end - bytes.begin
|
||||
end
|
||||
response.header["Accept-Ranges"] = "bytes"
|
||||
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes
|
||||
response.header["status"] = status_code
|
||||
|
||||
send_data IO.binread(path, length, offset), options
|
||||
else
|
||||
raise ActionController::MissingFile, "Cannot read file #{path}."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
|
||||
def create
|
||||
flag, msg = check_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
|
|
@ -27,17 +27,17 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def activate
|
||||
return render_error('请先认证') unless current_user.ci_certification?
|
||||
return tip_exception('请先认证') unless current_user.ci_certification?
|
||||
|
||||
begin
|
||||
@cloud_account = Ci::CloudAccount.find params[:id]
|
||||
ActiveRecord::Base.transaction do
|
||||
if @repo
|
||||
return render_error('该项目已经激活') if @repo.repo_active?
|
||||
return tip_exception('该项目已经激活') if @repo.repo_active?
|
||||
@repo.activate!(@project)
|
||||
else
|
||||
@repo = Ci::Repo.auto_create!(@ci_user, @project)
|
||||
|
|
@ -50,7 +50,7 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -59,39 +59,39 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
|
||||
def bind
|
||||
flag, msg = check_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def trustie_bind
|
||||
account = params[:account].to_s
|
||||
return render_error("account不能为空.") if account.blank?
|
||||
return tip_exception("account不能为空.") if account.blank?
|
||||
|
||||
flag, msg = check_trustie_bind_cloud_account!
|
||||
return render_error(msg) if flag === true
|
||||
return tip_exception(msg) if flag === true
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
@cloud_account = trustie_bind_account!
|
||||
if @cloud_account.blank?
|
||||
render_error('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
|
||||
raise ActiveRecord::Rollback
|
||||
else
|
||||
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
|
||||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def unbind
|
||||
|
|
@ -107,18 +107,18 @@ class Ci::CloudAccountsController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def oauth_grant
|
||||
password = params[:password].to_s
|
||||
return render_error('你输入的密码不正确.') unless current_user.check_password?(password)
|
||||
return tip_exception('你输入的密码不正确.') unless current_user.check_password?(password)
|
||||
|
||||
oauth = current_user.oauths.last
|
||||
return render_error("服务器出小差了.") if oauth.blank?
|
||||
return tip_exception("服务器出小差了.") if oauth.blank?
|
||||
|
||||
result = gitea_oauth_grant!(password, oauth)
|
||||
return render_error('授权失败.') unless result === true
|
||||
return tip_exception('授权失败.') unless result === true
|
||||
current_user.set_drone_step!(User::DEVOPS_CERTIFICATION)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
ActiveRecord::Base.transaction do
|
||||
size = Ci::Pipeline.where('branch=? and identifier=? and owner=?', params[:branch], params[:repo], params[:owner]).size
|
||||
if size > 0
|
||||
render_error("#{params[:branch]}分支已经存在流水线!")
|
||||
tip_exception("#{params[:branch]}分支已经存在流水线!")
|
||||
return
|
||||
end
|
||||
pipeline = Ci::Pipeline.new(pipeline_name: params[:pipeline_name], file_name: params[:file_name],owner: params[:owner],
|
||||
|
|
@ -53,7 +53,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok({id: pipeline.id})
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
# 在代码库创建文件
|
||||
|
|
@ -81,6 +81,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
repo_branch: pipeline.branch,
|
||||
repo_config: pipeline.file_name
|
||||
}
|
||||
Rails.logger.info("########create_params===#{create_params.to_json}")
|
||||
repo = Ci::Repo.create_repo(create_params)
|
||||
repo
|
||||
end
|
||||
|
|
@ -118,7 +119,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
|
@ -132,7 +133,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def content
|
||||
|
|
@ -182,7 +183,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage
|
||||
|
|
@ -192,7 +193,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def delete_stage
|
||||
|
|
@ -205,7 +206,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage_index(pipeline_id, show_index, diff)
|
||||
|
|
@ -229,7 +230,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
unless steps.empty?
|
||||
steps.each do |step|
|
||||
unless step[:template_id]
|
||||
render_error('请选择模板!')
|
||||
tip_exception('请选择模板!')
|
||||
return
|
||||
end
|
||||
if !step[:id]
|
||||
|
|
@ -246,7 +247,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def create_stage_step
|
||||
|
|
@ -262,7 +263,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update_stage_step
|
||||
|
|
@ -279,7 +280,7 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
render_ok
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def delete_stage_step
|
||||
|
|
@ -289,6 +290,6 @@ class Ci::PipelinesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,19 +30,19 @@ class Ci::ProjectsController < Ci::BaseController
|
|||
@file = interactor.result
|
||||
render_result(1, "更新成功")
|
||||
else
|
||||
render_error(interactor.error)
|
||||
tip_exception(interactor.error)
|
||||
end
|
||||
end
|
||||
|
||||
def activate
|
||||
return render_error('你还未认证') unless current_user.ci_certification?
|
||||
return tip_exception('你还未认证') unless current_user.ci_certification?
|
||||
|
||||
begin
|
||||
ActiveRecord::Base.transaction do
|
||||
if @repo
|
||||
return render_error('该项目已经激活') if @repo.repo_active?
|
||||
@repo.destroy! if @repo&.repo_user_id == 0
|
||||
return tip_exception('该项目已经激活') if @repo.repo_active?
|
||||
@repo.activate!(@project)
|
||||
return render_ok
|
||||
else
|
||||
@repo = Ci::Repo.auto_create!(@ci_user, @project)
|
||||
@ci_user.update_column(:user_syncing, false)
|
||||
|
|
@ -55,12 +55,12 @@ class Ci::ProjectsController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
end
|
||||
|
||||
def deactivate
|
||||
return render_error('该项目已经取消激活') if !@repo.repo_active?
|
||||
return tip_exception('该项目已经取消激活') if !@repo.repo_active?
|
||||
|
||||
@project.update_column(:open_devops, false)
|
||||
@repo.deactivate_repos!
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ class Ci::SecretsController < Ci::BaseController
|
|||
if result["id"]
|
||||
render_ok
|
||||
else
|
||||
render_error(result["message"])
|
||||
tip_exception(result["message"])
|
||||
end
|
||||
else
|
||||
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret
|
||||
if result["id"]
|
||||
render_ok
|
||||
else
|
||||
render_error(result["message"])
|
||||
tip_exception(result["message"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -39,7 +39,7 @@ class Ci::SecretsController < Ci::BaseController
|
|||
Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], {name: name}).delete_secret
|
||||
render_ok
|
||||
else
|
||||
render_error("参数名不能为空")
|
||||
tip_exception("参数名不能为空")
|
||||
end
|
||||
rescue Exception => ex
|
||||
render_ok
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def update
|
||||
|
|
@ -63,7 +63,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
)
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
|
@ -73,7 +73,7 @@ class Ci::TemplatesController < Ci::BaseController
|
|||
end
|
||||
render_ok
|
||||
rescue Exception => ex
|
||||
render_error(ex.message)
|
||||
tip_exception(ex.message)
|
||||
end
|
||||
|
||||
#======流水线模板查询=====#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
class CommitLogsController < ApplicationController
|
||||
|
||||
def create
|
||||
tip_exception "未认证" unless params[:token].to_s == "7917908927b6f1b792f2027a08a8b24a2de42c1692c2fd45da0dee5cf90a5af5"
|
||||
ref = params[:ref]
|
||||
user_name = params[:pusher][:login]
|
||||
user_mail = params[:pusher][:email]
|
||||
user = User.find_by(mail: user_mail)
|
||||
user = User.find_by(login: user_name) if user.blank?
|
||||
|
||||
repository_id = params[:repository][:id]
|
||||
repository_name = params[:repository][:name]
|
||||
repository_full_name = params[:repository][:full_name]
|
||||
owner_name = repository_full_name.split("/")[0]
|
||||
owner = User.find_by(login: owner_name)
|
||||
project = Project.where(identifier: repository_name).where(user_id: owner&.id)&.first
|
||||
project = Project.where(identifier: repository_name).where(gpid: repository_id)&.first if project.blank?
|
||||
project.update_column(:updated_on, Time.now) if project.present?
|
||||
params[:commits].each do |commit|
|
||||
commit_id = commit[:id]
|
||||
message = commit[:message]
|
||||
CommitLog.create(user: user, project: project, repository_id: repository_id,
|
||||
name: repository_name, full_name: repository_full_name,
|
||||
ref: ref, commit_id: commit_id, message: message)
|
||||
# 统计数据新增
|
||||
CacheAsyncSetJob.perform_later("project_common_service", {commits: 1}, project.id)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -6,9 +6,14 @@ class CompareController < ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
load_compare_params
|
||||
compare
|
||||
@merge_status, @merge_message = get_merge_message
|
||||
if params[:type] == "sha"
|
||||
load_compare_params
|
||||
@compare_result ||= gitea_compare(@base, @head)
|
||||
else
|
||||
load_compare_params
|
||||
compare
|
||||
@merge_status, @merge_message = get_merge_message
|
||||
end
|
||||
@page_size = page_size <= 0 ? 1 : page_size
|
||||
@page_limit = page_limit <=0 ? 15 : page_limit
|
||||
@page_offset = (@page_size -1) * @page_limit
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ module Acceleratorable
|
|||
end
|
||||
|
||||
def accelerator_domain
|
||||
Gitea.gitea_config[:accelerator]["domain"]
|
||||
GiteaService.gitea_config[:accelerator]["domain"]
|
||||
end
|
||||
|
||||
def accelerator_username
|
||||
Gitea.gitea_config[:accelerator]["access_key_id"]
|
||||
GiteaService.gitea_config[:accelerator]["access_key_id"]
|
||||
end
|
||||
|
||||
def config_accelerator?
|
||||
Gitea.gitea_config[:accelerator].present?
|
||||
GiteaService.gitea_config[:accelerator].present?
|
||||
end
|
||||
|
||||
def is_foreign_url?(clone_addr)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
module Api::ProjectHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def load_project
|
||||
namespace = params[:owner]
|
||||
repo = params[:repo]
|
||||
|
||||
@project, @owner = Project.find_with_namespace(namespace, repo)
|
||||
|
||||
if @project
|
||||
logger.info "###########:project founded"
|
||||
@project
|
||||
else
|
||||
logger.info "###########:project not found"
|
||||
@project = nil
|
||||
render_not_found and return
|
||||
end
|
||||
@project
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
module Api::PullHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def load_pull_request
|
||||
pull_request_id = params[:pull_id] || params[:id]
|
||||
@pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || PullRequest.find_by_id(pull_request_id)
|
||||
@issue = @pull_request&.issue
|
||||
if @pull_request
|
||||
logger.info "###########pull_request founded"
|
||||
@pull_request
|
||||
else
|
||||
logger.info "###########pull_request not found"
|
||||
@pull_request = nil
|
||||
render_not_found and return
|
||||
end
|
||||
|
||||
@pull_request
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
module Api::UserHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def load_observe_user
|
||||
username = params[:owner]
|
||||
|
||||
@observe_user = User.find_by(login: username)
|
||||
|
||||
if @observe_user
|
||||
logger.info "###########observe_user not founded"
|
||||
@observe_user
|
||||
else
|
||||
logger.info "###########observe_user not found"
|
||||
@observe_user = nil
|
||||
render_not_found and return
|
||||
end
|
||||
@observe_user
|
||||
end
|
||||
|
||||
# 是否具有查看用户或编辑用户的权限
|
||||
def check_auth_for_observe_user
|
||||
return render_forbidden unless current_user.admin? || @observe_user.id == current_user.id
|
||||
end
|
||||
|
||||
def strip(str)
|
||||
str.to_s.strip.presence
|
||||
end
|
||||
end
|
||||
|
|
@ -160,9 +160,9 @@ module Ci::CloudAccountManageable
|
|||
state = SecureRandom.hex(8)
|
||||
# redirect_uri eg:
|
||||
# https://localhost:3000/login/oauth/authorize?client_id=94976481-ad0e-4ed4-9247-7eef106007a2&redirect_uri=http%3A%2F%2F121.69.81.11%3A80%2Flogin&response_type=code&state=9cab990b9cfb1805
|
||||
redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login")
|
||||
clientId = client_id(oauth)
|
||||
grant_url = "#{Gitea.gitea_config[:domain]}/login/oauth/authorize?client_id=#{clientId}&redirect_uri=#{redirect_uri}&response_type=code&state=#{state}"
|
||||
# redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login")
|
||||
# clientId = client_id(oauth)
|
||||
grant_url = "#{@cloud_account.drone_url}/login"
|
||||
logger.info "[gitea] grant_url: #{grant_url}"
|
||||
|
||||
conn = Faraday.new(url: grant_url) do |req|
|
||||
|
|
@ -179,7 +179,7 @@ module Ci::CloudAccountManageable
|
|||
|
||||
def drone_oauth_user!(url, state)
|
||||
logger.info "[drone] drone_oauth_user url: #{url}"
|
||||
conn = Faraday.new(url: "#{Gitea.gitea_config[:domain]}#{url}") do |req|
|
||||
conn = Faraday.new(url: url) do |req|
|
||||
req.request :url_encoded
|
||||
req.adapter Faraday.default_adapter
|
||||
req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}"
|
||||
|
|
@ -188,8 +188,8 @@ module Ci::CloudAccountManageable
|
|||
response = conn.get
|
||||
logger.info "[drone] response headers: #{response.headers}"
|
||||
|
||||
true
|
||||
# response.headers['location'].include?('error') ? false : true
|
||||
# true
|
||||
response.headers['location'].include?('error') ? false : true
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module LoginHelper
|
|||
|
||||
def set_autologin_cookie(user)
|
||||
token = Token.get_or_create_permanent_login_token(user, "autologin")
|
||||
sync_user_token_to_trustie(user.login, token.value)
|
||||
# sync_user_token_to_trustie(user.login, token.value)
|
||||
|
||||
Rails.logger.info "###### def set_autologin_cookie and get_or_create_permanent_login_token result: #{token&.value}"
|
||||
cookie_options = {
|
||||
|
|
@ -116,6 +116,7 @@ module LoginHelper
|
|||
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash))
|
||||
if interactor.success?
|
||||
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########"
|
||||
user.update_column(:is_sync_pwd, true)
|
||||
true
|
||||
else
|
||||
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea fail!: #{interactor.error}"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
module RegisterHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def autologin_register(username, email, password, platform= 'forge')
|
||||
def autologin_register(username, email, password, platform = 'forge', phone = nil, nickname =nil, need_edit_info = false)
|
||||
result = {message: nil, user: nil}
|
||||
email = email.blank? ? "#{username}@example.org" : email
|
||||
|
||||
user = User.new(admin: false, login: username, mail: email, type: "User")
|
||||
user.password = password
|
||||
user.platform = platform
|
||||
user.activate
|
||||
|
||||
user.phone = phone if phone.present?
|
||||
user.nickname = nickname if nickname.present?
|
||||
if need_edit_info
|
||||
user.need_edit_info
|
||||
else
|
||||
user.activate
|
||||
end
|
||||
|
||||
return unless user.valid?
|
||||
|
||||
create_gitea_user!(user, username, email, password)
|
||||
|
|
@ -51,7 +59,7 @@ module RegisterHelper
|
|||
user.password = params[:password]
|
||||
user.mail = params[:email]
|
||||
|
||||
if user.save!
|
||||
if user.save!
|
||||
sync_params = {
|
||||
password: params[:password].to_s,
|
||||
email: params[:email],
|
||||
|
|
@ -59,9 +67,9 @@ module RegisterHelper
|
|||
new_name: params[:username],
|
||||
source_id: 0
|
||||
}
|
||||
|
||||
|
||||
interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params)
|
||||
if interactor.success?
|
||||
if interactor.success?
|
||||
result[:user] = user
|
||||
else
|
||||
result[:message] = '用户同步Gitea失败!'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,17 @@ module Repository::LanguagesPercentagable
|
|||
result = Gitea::Repository::Languages::ListService.call(@owner.login,
|
||||
@repository.identifier, current_user&.gitea_token)
|
||||
|
||||
@transform_language = result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
|
||||
@transform_language = @transform_language = result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
|
||||
update_project_language(@transform_language) unless @transform_language.nil?
|
||||
@transform_language
|
||||
end
|
||||
|
||||
def update_project_language(language)
|
||||
return if @project.project_language.present?
|
||||
db_language = ProjectLanguage.find_or_create_by!(name: language.keys.first.downcase.upcase_first)
|
||||
@project.update_column(:project_language_id, db_language.id)
|
||||
rescue
|
||||
return
|
||||
update_project_language(@transform_language) unless @transform_language.nil?
|
||||
@transform_language
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController
|
|||
|
||||
|
||||
def index
|
||||
issue_tags = @project.issue_tags.reorder("#{order_name} #{order_type}")
|
||||
issue_tags = @project.issue_tags.includes(:issues).reorder("issue_tags.#{order_name} #{order_type}")
|
||||
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
|
||||
@page = params[:page] || 1
|
||||
@limit = params[:limit] || 15
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
class MarkFilesController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :load_project
|
||||
before_action :load_pull_request
|
||||
|
||||
def index
|
||||
@files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token, { "only-file-name": true })
|
||||
@mark_files = MarkFile.where(pull_request_id: @pull_request.id)
|
||||
end
|
||||
|
||||
def create
|
||||
# unless @pull_request.mark_files.present?
|
||||
# MarkFile.bulk_insert(*%i[pull_request_id, file_path_sha file_path created_at updated_at]) do |worker|
|
||||
# @files_result['Files'].each do |file|
|
||||
# worker.add(pull_request_id: @pull_request.id, file_path_sha: SecureRandom.uuid.gsub("-", ""), file_path: file['Name'])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
def mark_file_as_unread
|
||||
tip_exception "参数错误" if params[:file_path_sha].blank?
|
||||
file_path = Base64.strict_decode64(params[:file_path_sha].to_s)
|
||||
mark_file = @pull_request.mark_files.find_or_initialize_by(file_path_sha: params[:file_path_sha])
|
||||
mark_file.file_path = file_path
|
||||
mark_file.user_id = current_user.id
|
||||
mark_file.mark_as_read = false
|
||||
mark_file.save
|
||||
render_ok
|
||||
rescue Exception => e
|
||||
tip_exception "参数解析错误"
|
||||
end
|
||||
|
||||
def mark_file_as_read
|
||||
tip_exception "参数错误" if params[:file_path_sha].blank?
|
||||
file_path = Base64.strict_decode64(params[:file_path_sha].to_s)
|
||||
mark_file = @pull_request.mark_files.find_or_initialize_by(file_path_sha: params[:file_path_sha])
|
||||
mark_file.file_path = file_path
|
||||
mark_file.user_id = current_user.id
|
||||
mark_file.mark_as_read = true
|
||||
mark_file.save
|
||||
render_ok
|
||||
rescue Exception => e
|
||||
tip_exception "参数解析错误"
|
||||
end
|
||||
|
||||
private
|
||||
def review_params
|
||||
params.require(:review).permit(:content, :commit_id, :status)
|
||||
end
|
||||
|
||||
def load_pull_request
|
||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -34,6 +34,9 @@ class MembersController < ApplicationController
|
|||
|
||||
@total_count = scope.size
|
||||
@members = paginate(scope)
|
||||
if @project.owner.is_a?(Organization) && (params[:page].to_i == 1 || params[:page].blank?) && !@project.members.exists?(user_id: current_user.id)
|
||||
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
|
||||
end
|
||||
end
|
||||
|
||||
def remove
|
||||
|
|
@ -69,11 +72,14 @@ class MembersController < ApplicationController
|
|||
end
|
||||
|
||||
def check_member_exists!
|
||||
return render_error("user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists?
|
||||
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
|
||||
return render_error("#{@user&.nickname}已经是项目成员") if member_exists? || (params[:user_id].to_i == current_user.id && @current_user_header_team.present?)
|
||||
end
|
||||
|
||||
def check_member_not_exists!
|
||||
return render_error("user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists?
|
||||
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
|
||||
return render_error("用户为组织成员,请到组织下操作!") if (params[:user_id].to_i == current_user.id && @current_user_header_team.present?) && !member_exists?
|
||||
return render_error("#{@user&.nickname}还不是项目成员") unless member_exists?
|
||||
end
|
||||
|
||||
def check_user_profile_completed
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
class NoticesController < ApplicationController
|
||||
|
||||
def create
|
||||
tip_exception("参数有误") if params["source"].blank?
|
||||
return tip_exception("参数有误") if params["source"].blank?
|
||||
user_id = params[:user_id]
|
||||
|
||||
if params["source"] == "CompetitionBegin"
|
||||
|
|
@ -13,9 +13,21 @@ class NoticesController < ApplicationController
|
|||
elsif params["source"] == "CompetitionReview"
|
||||
competition_id = params[:competition_id]
|
||||
SendTemplateMessageJob.perform_later('CompetitionReview', user_id, competition_id)
|
||||
elsif params["source"] == "CustomTip"
|
||||
users_id = params[:users_id]
|
||||
props = params[:props].to_unsafe_hash
|
||||
return tip_exception("参数有误") unless props.is_a?(Hash) && users_id.is_a?(Array)
|
||||
template_id = params[:template_id]
|
||||
SendTemplateMessageJob.perform_later('CustomTip', users_id, template_id, props)
|
||||
else
|
||||
tip_exception("#{params["source"]}未配置")
|
||||
end
|
||||
render_ok
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def params_props
|
||||
params.require(:notice).permit(:props)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
class NpsController < ApplicationController
|
||||
|
||||
before_action :require_login
|
||||
|
||||
# close,关闭
|
||||
# createIssue,创建issue
|
||||
# createPullRequest,创建PR
|
||||
# auditPullRequest,审核PR
|
||||
# indexProject,项目主页
|
||||
# createProject,创建项目
|
||||
# createOrganization,创建组织
|
||||
def create
|
||||
tip_exception "缺少参数" if params[:action_id].blank? || params[:action_type].blank?
|
||||
UserNp.create(:action_id => params[:action_id].to_i, :action_type => params[:action_type], :user_id => User.current.id, :score => params[:score].to_f, memo: params[:memo])
|
||||
render_ok
|
||||
end
|
||||
end
|
||||
|
|
@ -2,6 +2,7 @@ class Oauth::BaseController < ActionController::Base
|
|||
include RenderHelper
|
||||
include LoginHelper
|
||||
include ControllerRescueHandler
|
||||
include RegisterHelper
|
||||
# include LaboratoryHelper
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
|
@ -22,7 +23,7 @@ class Oauth::BaseController < ActionController::Base
|
|||
end
|
||||
|
||||
def auth_hash
|
||||
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
|
||||
# Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
|
||||
request.env['omniauth.auth']
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
class Oauth::CallbacksController < Oauth::BaseController
|
||||
def create
|
||||
process_callback
|
||||
rescue Exception => e
|
||||
Rails.logger.info "授权失败:#{e}"
|
||||
tip_exception("授权失败")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def config_providers
|
||||
config = Rails.application.config_for(:configuration)
|
||||
config.dig("oauth").keys
|
||||
end
|
||||
|
||||
# QQ: {"ret":0,"msg":"","is_lost":0,"nickname":"颜值不算太高","gender":"男","gender_type":1,"province":"","city":"","year":"2013","constellation":"","figureurl":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/30","figureurl_1":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/50","figureurl_2":"http://qzapp.qlogo.cn/qzapp/101508858/0F860F4B329768F47B22341C5FD9089C/100","figureurl_qq_1":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=40\u0026t=1568887757","figureurl_qq_2":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=100\u0026t=1568887757","figureurl_qq":"http://thirdqq.qlogo.cn/g?b=oidb\u0026k=My3segFVHFqVmauibJQUltw\u0026s=140\u0026t=1568887757","figureurl_type":"1","is_yellow_vip":"0","vip":"0","yellow_vip_level":"0","level":"0","is_yellow_year_vip":"0"}
|
||||
def process_callback
|
||||
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
|
||||
if auth_hash.blank?
|
||||
redirect_to("/login") && return
|
||||
end
|
||||
|
||||
new_user = false
|
||||
platform = auth_hash[:provider]
|
||||
uid = auth_hash[:uid]
|
||||
mail = auth_hash.info.email || nil
|
||||
nickname = ["gitee", "github"].include?(platform) ? auth_hash.info.name : auth_hash.info.nickname
|
||||
|
||||
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.find_by(uid: uid)
|
||||
if open_user.present? && open_user.user.present?
|
||||
successful_authentication(open_user.user)
|
||||
else
|
||||
if current_user.blank? || !current_user.logged?
|
||||
has_user = User.find_by(mail: mail)
|
||||
if has_user.present?
|
||||
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: has_user.id, uid: uid, extra: auth_hash.extra)
|
||||
successful_authentication(has_user)
|
||||
else
|
||||
new_user = true
|
||||
login = build_login_name(platform, auth_hash.info.nickname)
|
||||
mail = "#{login}@example.org" if mail.blank?
|
||||
code = %W(0 1 2 3 4 5 6 7 8 9)
|
||||
rand_password = code.sample(10).join
|
||||
reg_result = autologin_register(login, mail, rand_password, platform, nil, nickname)
|
||||
Rails.logger.info("[OAuth2] omniauth.auth [reg_result] #{reg_result} ")
|
||||
if reg_result[:message].blank?
|
||||
open_user = "OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user_id: reg_result[:user][:id], uid: uid, extra: auth_hash.extra)
|
||||
successful_authentication(open_user.user)
|
||||
else
|
||||
tip_exception(reg_result.present? ? reg_result[:message] : "授权失败")
|
||||
end
|
||||
end
|
||||
else
|
||||
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: login, extra: auth_hash.extra)
|
||||
end
|
||||
end
|
||||
redirect_to root_path(new_user: new_user)
|
||||
end
|
||||
|
||||
# gitee,github nickname=login,如果系统未占用保留原用户名
|
||||
def build_login_name(provider, nickname)
|
||||
if ["gitee", "github"].include?(provider) && User.find_by(login: nickname).blank?
|
||||
nickname
|
||||
else
|
||||
User.generate_user_login('p')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
class ObRepositorySyncsController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :load_project
|
||||
before_action :load_ob_repository_sync, except: [:create]
|
||||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render_ok(data: @ob_repository_sync)
|
||||
end
|
||||
|
||||
|
||||
def create
|
||||
tip_exception "参数错误" if params[:github_address].blank? && params[:gitee_address].blank?
|
||||
project_name ="#{@project.owner.name}:#{@project.identifier}"
|
||||
service = ObRepositorySync::ApiService.new(project_name)
|
||||
domain = GiteaService.gitea_config[:domain]
|
||||
project_params = params.merge({ "gitlink_address": "#{domain}/#{@project.owner&.login}/#{@project.identifier}.git" })
|
||||
res = service.create_projects(project_params)
|
||||
tip_exception "保存失败: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
sync_id = res["data"]["id"]
|
||||
ob_repository_sync = ObRepositorySync.find_or_initialize_by(project_id: @project.id)
|
||||
ob_repository_sync.project_id = @project.id
|
||||
ob_repository_sync.user_id = current_user.id
|
||||
ob_repository_sync.name = project_name
|
||||
ob_repository_sync.github_address = "#{params[:github_address]}"
|
||||
ob_repository_sync.gitee_address = "#{params[:gitee_address]}"
|
||||
ob_repository_sync.github_token = "#{params[:github_token]}"
|
||||
ob_repository_sync.gitee_token = "#{params[:gitee_token]}"
|
||||
ob_repository_sync.sync_id = sync_id
|
||||
ob_repository_sync.save!
|
||||
render_ok
|
||||
end
|
||||
|
||||
def delete
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.delete_project @ob_repository_sync.sync_id
|
||||
tip_exception "删除失败: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
if res["code"].to_s == "200"
|
||||
@ob_repository_sync.destroy!
|
||||
end
|
||||
render_ok
|
||||
end
|
||||
|
||||
def jobs
|
||||
tip_exception "该项目未创建同步任务" if @ob_repository_sync.blank?
|
||||
page = params[:page] || 1
|
||||
limit = params[:limit] || 10
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
source = ""
|
||||
if params[:type] && params[:type].to_s.downcase == "github"
|
||||
source = "github_branch"
|
||||
elsif params[:type] && params[:type].to_s.downcase == "gitee"
|
||||
source = "gitee_branch"
|
||||
end
|
||||
res = service.get_projects_jobs(source, page, limit)
|
||||
data = res["data"]["list"]
|
||||
render_ok(count: res["data"]["total"], data: data)
|
||||
end
|
||||
|
||||
def create_jobs
|
||||
tip_exception "必须配置一个分支" if params[:github_branch].blank? && params[:gitee_branch].blank? && params[:gitlink_branch].blank?
|
||||
ob_jobs = ObRepositorySyncJob.where(ob_repository_sync_id: @ob_repository_sync.id)
|
||||
ob_jobs = ob_jobs.where(job_type: params[:job_type]) if params[:job_type].present?
|
||||
ob_jobs = ob_jobs.where(github_branch: params[:github_branch]) if params[:github_branch].present?
|
||||
ob_jobs = ob_jobs.where(gitee_branch: params[:gitee_branch]) if params[:gitee_branch].present?
|
||||
ob_jobs = ob_jobs.where(gitlink_branch: params[:gitlink_branch]) if params[:gitlink_branch].present?
|
||||
tip_exception "该分支组合已配置,不能重复!" if ob_jobs.count > 0
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.create_projects_jobs(params)
|
||||
tip_exception "保存失败: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
job_id = res["data"]["id"]
|
||||
job = ObRepositorySyncJob.new
|
||||
job.ob_repository_sync_id = @ob_repository_sync.id
|
||||
job.github_branch = "#{params[:github_branch]}"
|
||||
job.gitee_branch = "#{params[:gitee_branch]}"
|
||||
job.gitlink_branch = "#{params[:gitlink_branch]}"
|
||||
job.job_type = "#{params[:job_type]}"
|
||||
job.base = "#{params[:base]}"
|
||||
job.job_id = job_id
|
||||
job.save
|
||||
render_ok
|
||||
end
|
||||
|
||||
|
||||
def delete_job
|
||||
tip_exception "缺少参数job_id" if params[:job_id].blank?
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.delete_job params[:job_id]
|
||||
tip_exception "删除失败: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
job = ObRepositorySyncJob.find_by(ob_repository_sync_id: @ob_repository_sync.id, job_id: params[:job_id])
|
||||
job.destroy! if job.present?
|
||||
render_ok
|
||||
end
|
||||
|
||||
def start_job
|
||||
tip_exception "缺少参数job_id" if params[:job_id].blank?
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.start_job params[:job_id]
|
||||
tip_exception "启动错误: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
render_ok
|
||||
end
|
||||
|
||||
def stop_job
|
||||
tip_exception "缺少参数job_id" if params[:job_id].blank?
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.stop_job params[:job_id]
|
||||
tip_exception "停止错误: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
render_ok
|
||||
end
|
||||
|
||||
def job_logs
|
||||
tip_exception "该项目未创建同步任务" if @ob_repository_sync.blank?
|
||||
tip_exception "缺少参数job_id" if params[:job_id].blank?
|
||||
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
|
||||
res = service.job_logs params[:job_id]
|
||||
tip_exception "请求错误: #{res["msg"]}" if res["code"].to_s != "200"
|
||||
render_ok(count: res["data"]["total"], data: res["data"]["list"])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_ob_repository_sync
|
||||
@ob_repository_sync = ObRepositorySync.find_by(project_id: @project.id)
|
||||
end
|
||||
|
||||
def authenticate_user!
|
||||
return if @project.member?(current_user) || current_user.admin?
|
||||
render_forbidden('你没有权限操作')
|
||||
end
|
||||
end
|
||||
|
|
@ -4,12 +4,14 @@ class Organizations::OrganizationUsersController < Organizations::BaseController
|
|||
|
||||
def index
|
||||
@organization_users = @organization.organization_users.includes(:user)
|
||||
search = params[:search].to_s.downcase
|
||||
user_condition_users = User.like(search).to_sql
|
||||
team_condition_teams = User.joins(:teams).merge(@organization.teams.like(search)).to_sql
|
||||
users = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users")
|
||||
|
||||
@organization_users = @organization_users.where(user_id: users).distinct
|
||||
if params[:search].present?
|
||||
search = params[:search].to_s.downcase
|
||||
user_condition_users = User.like(search).to_sql
|
||||
team_condition_teams = User.joins(:teams).merge(@organization.teams.like(search)).to_sql
|
||||
users = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users")
|
||||
|
||||
@organization_users = @organization_users.where(user_id: users).distinct
|
||||
end
|
||||
|
||||
@organization_users = kaminari_paginate(@organization_users)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
|
|||
Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate!
|
||||
@organization = Organizations::CreateService.call(current_user, organization_params)
|
||||
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
|
||||
Cache::V2::OwnerCommonService.new(@organization.id).reset
|
||||
end
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
|
|
@ -48,6 +49,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
|
|||
|
||||
Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload)
|
||||
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
|
||||
Cache::V2::OwnerCommonService.new(@organization.id).reset
|
||||
end
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
|
|||
tip_exception(e.message)
|
||||
end
|
||||
|
||||
def create_all
|
||||
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
|
||||
ActiveRecord::Base.transaction do
|
||||
@organization.projects.each do |project|
|
||||
TeamProject.build(@organization.id, @team.id, project.id)
|
||||
end
|
||||
Gitea::Organization::TeamProject::CreateAllService.call(@organization.gitea_token, @team.gtid, @organization.login)
|
||||
render_ok
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
|
||||
ActiveRecord::Base.transaction do
|
||||
|
|
@ -33,6 +44,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
|
|||
tip_exception(e.message)
|
||||
end
|
||||
|
||||
def destroy_all
|
||||
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
|
||||
ActiveRecord::Base.transaction do
|
||||
@team.team_projects.each do |project|
|
||||
project.destroy!
|
||||
end
|
||||
Gitea::Organization::TeamProject::DeleteAllService.call(@organization.gitea_token, @team.gtid, @organization.login)
|
||||
render_ok
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def load_organization
|
||||
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
|
||||
|
|
@ -47,7 +69,7 @@ class Organizations::TeamProjectsController < Organizations::BaseController
|
|||
end
|
||||
|
||||
def load_operate_project
|
||||
@operate_project = Project.find_by(id: project_mark) || Project.find_by(identifier: project_mark)
|
||||
@operate_project = @organization.projects.where(id: project_mark).take || @organization.projects.where(identifier: project_mark).take
|
||||
tip_exception("项目不存在") if @operate_project.nil?
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ProjectCategoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def group_list
|
||||
@project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc)
|
||||
@project_categories = ProjectCategory.select("id, name, projects_count, private_projects_count, (projects_count - private_projects_count) as public_projects_count").having('public_projects_count > 0').order(public_projects_count: :desc)
|
||||
# projects = Project.no_anomory_projects.visible
|
||||
# @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
class Projects::ProjectInviteLinksController < Projects::BaseController
|
||||
before_action :require_manager!, except: [:show_link, :redirect_link]
|
||||
before_action :require_login
|
||||
|
||||
def current_link
|
||||
role = params[:role]
|
||||
is_apply = params[:is_apply]
|
||||
return render_error('请输入正确的参数!') unless role.present? && is_apply.present?
|
||||
@project_invite_link = ProjectInviteLink.find_by(user_id: current_user.id, project_id: @project.id, role: role, is_apply: is_apply)
|
||||
@project_invite_link = ProjectInviteLink.build!(@project, current_user, role, is_apply) unless @project_invite_link.present?
|
||||
end
|
||||
|
||||
def generate_link
|
||||
ActiveRecord::Base.transaction do
|
||||
params_data = link_params.merge({user_id: current_user.id, project_id: @project.id})
|
||||
Projects::ProjectInviteLinks::CreateForm.new(params_data).validate!
|
||||
@project_invite_link = ProjectInviteLink.build!(project, user, params_data[:role], params_data[:is_apply])
|
||||
end
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
tip_exception(e.message)
|
||||
end
|
||||
|
||||
def show_link
|
||||
@project_invite_link = ProjectInviteLink.find_by(sign: params[:invite_sign])
|
||||
return render_not_found unless @project_invite_link.present?
|
||||
end
|
||||
|
||||
def redirect_link
|
||||
Projects::LinkJoinService.call(current_user, @project, params[:invite_sign])
|
||||
render_ok
|
||||
rescue Exception => e
|
||||
uid_logger_error(e.message)
|
||||
normal_status(-1, e.message)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def link_params
|
||||
params.require(:project_invite_link).permit(:role, :is_apply)
|
||||
end
|
||||
end
|
||||
|
|
@ -9,7 +9,7 @@ class Projects::WebhooksController < Projects::BaseController
|
|||
|
||||
def create
|
||||
ActiveRecord::Base.transaction do
|
||||
return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 19
|
||||
return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 49
|
||||
return render_error("参数错误.") unless webhook_params.present?
|
||||
form = Projects::Webhooks::CreateForm.new(webhook_params)
|
||||
return render json: {status: -1, message: form.errors} unless form.validate!
|
||||
|
|
|
|||
|
|
@ -40,8 +40,9 @@ class ProjectsController < ApplicationController
|
|||
category_id = params[:category_id]
|
||||
@total_count =
|
||||
if category_id.blank?
|
||||
ps = ProjectStatistic.first
|
||||
ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
|
||||
# ps = ProjectStatistic.first
|
||||
# ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
|
||||
@projects.total_count
|
||||
else
|
||||
cate = ProjectCategory.find_by(id: category_id)
|
||||
cate&.projects_count || 0
|
||||
|
|
@ -161,6 +162,15 @@ class ProjectsController < ApplicationController
|
|||
}
|
||||
gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params)
|
||||
@project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]})
|
||||
# 更新对应所属分类下的项目数量(私有)
|
||||
before_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][0] : @project.is_public
|
||||
after_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][1] : @project.is_public
|
||||
before_pc_id = @project.previous_changes[:project_category_id].present? ? @project.previous_changes[:project_category_id][0] : @project.project_category_id
|
||||
after_pc_id = @project.previous_changes[:project_category_id].present? ? @project.previous_changes[:project_category_id][1] : @project.project_category_id
|
||||
before_pc = ProjectCategory.find_by_id(before_pc_id)
|
||||
after_pc = ProjectCategory.find_by_id(after_pc_id)
|
||||
before_pc.decrement!(:private_projects_count, 1) if before_pc.present? && !before_is_public
|
||||
after_pc.increment!(:private_projects_count, 1) if after_pc.present? && !after_is_public
|
||||
end
|
||||
SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public, :identifier)) if Site.has_notice_menu?
|
||||
end
|
||||
|
|
@ -178,6 +188,8 @@ class ProjectsController < ApplicationController
|
|||
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
|
||||
@project.destroy!
|
||||
@project.forked_projects.update_all(forked_from_project_id: nil)
|
||||
# 如果该项目有所属的项目分类以及为私有项目,需要更新对应数量
|
||||
@project.project_category.decrement!(:private_projects_count, 1) if @project.project_category.present? && !@project.is_public
|
||||
render_ok
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ class PullRequestsController < ApplicationController
|
|||
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
|
||||
if @gitea_pull_request[:status] == :success
|
||||
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
|
||||
reviewers = User.where(id: params[:reviewer_ids])
|
||||
@pull_request.reviewers = reviewers
|
||||
SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
|
||||
SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
|
||||
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
|
||||
|
|
@ -93,19 +95,8 @@ class PullRequestsController < ApplicationController
|
|||
Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate!
|
||||
merge_params
|
||||
|
||||
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
|
||||
if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
|
||||
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
|
||||
return normal_status(-1, "最多只能创建一个标记。")
|
||||
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
|
||||
@issue&.issue_tags_relates&.destroy_all
|
||||
params[:issue_tag_ids].each do |tag|
|
||||
IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
|
||||
end
|
||||
else
|
||||
return normal_status(-1, "请输入正确的标记。")
|
||||
end
|
||||
end
|
||||
reviewers = User.where(id: params[:reviewer_ids])
|
||||
@pull_request.reviewers = reviewers
|
||||
|
||||
if @issue.update_attributes(@issue_params)
|
||||
if @pull_request.update_attributes(@local_params.compact)
|
||||
|
|
@ -155,6 +146,8 @@ class PullRequestsController < ApplicationController
|
|||
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
|
||||
if colsed === true
|
||||
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE)
|
||||
# 合并请求下issue处理为关闭
|
||||
@issue&.update_attributes!({status_id:5})
|
||||
SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
|
||||
normal_status(1, "已拒绝")
|
||||
else
|
||||
|
|
@ -176,6 +169,7 @@ class PullRequestsController < ApplicationController
|
|||
@issue_assign_to = @issue.get_assign_user
|
||||
@gitea_pull = Gitea::PullRequest::GetService.call(@owner.login,
|
||||
@repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
|
||||
@last_review = @pull_request.reviews.take
|
||||
end
|
||||
|
||||
def pr_merge
|
||||
|
|
@ -199,6 +193,8 @@ class PullRequestsController < ApplicationController
|
|||
# @pull_request.project_trend_status!
|
||||
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
|
||||
@issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
|
||||
# 合并请求下issue处理为关闭
|
||||
@issue&.update_attributes!({status_id:5})
|
||||
SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?
|
||||
normal_status(1, "合并成功")
|
||||
else
|
||||
|
|
@ -262,7 +258,7 @@ class PullRequestsController < ApplicationController
|
|||
|
||||
def get_relatived
|
||||
@project_tags = @project.issue_tags&.select(:id,:name, :color).as_json
|
||||
@project_versions = @project.versions&.select(:id,:name, :status).as_json
|
||||
@project_versions = @project.versions.opening&.select(:id,:name, :status).as_json
|
||||
@project_members = @project.all_developers
|
||||
@project_priories = IssuePriority&.select(:id,:name, :position).as_json
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class RepositoriesController < ApplicationController
|
|||
before_action :load_repository
|
||||
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
|
||||
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
|
||||
before_action :get_ref, only: %i[entries sub_entries top_counts file archive]
|
||||
before_action :get_ref, only: %i[entries sub_entries top_counts files archive]
|
||||
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
|
||||
before_action :get_statistics, only: %i[top_counts]
|
||||
|
||||
|
|
@ -47,14 +47,19 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def entries
|
||||
@project.increment!(:visits)
|
||||
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
|
||||
@week_project_visit_record, @month_project_visit_record = TimeableVisitRecord.build(@project.id)
|
||||
if @week_project_visit_record.visits < 300 && @month_project_visit_record.visits < 1000
|
||||
@week_project_visit_record.increment!(:visits)
|
||||
@month_project_visit_record.increment!(:visits)
|
||||
@project.increment!(:visits)
|
||||
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
|
||||
end
|
||||
if @project.educoder?
|
||||
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
|
||||
else
|
||||
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
|
||||
@entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
|
||||
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
|
||||
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -99,7 +104,7 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
end
|
||||
else
|
||||
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
|
||||
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
|
||||
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
|
||||
if interactor.success?
|
||||
result = interactor.result
|
||||
|
|
@ -137,13 +142,24 @@ class RepositoriesController < ApplicationController
|
|||
else
|
||||
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
|
||||
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
|
||||
render_error(@commit[:message], @commit[:status]) if @commit.has_key?(:status) || @commit_diff.has_key?(:status)
|
||||
end
|
||||
end
|
||||
|
||||
def tags
|
||||
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
|
||||
if params[:only_name].present?
|
||||
result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
|
||||
|
||||
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
|
||||
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
|
||||
else
|
||||
name_result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
|
||||
|
||||
@tag_names = result.is_a?(Hash) && result.key?(:status) ? [] : name_result
|
||||
|
||||
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
|
||||
|
||||
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
|
||||
end
|
||||
end
|
||||
|
||||
def contributors
|
||||
|
|
@ -222,7 +238,7 @@ class RepositoriesController < ApplicationController
|
|||
else
|
||||
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
|
||||
end
|
||||
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
|
||||
@path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
|
||||
@readme = result[:status] === :success ? result[:body] : nil
|
||||
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
|
||||
@readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
|
||||
|
|
@ -240,8 +256,8 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def archive
|
||||
domain = Gitea.gitea_config[:domain]
|
||||
api_url = Gitea.gitea_config[:base_url]
|
||||
domain = GiteaService.gitea_config[:domain]
|
||||
api_url = GiteaService.gitea_config[:base_url]
|
||||
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
|
||||
|
||||
file_path = [domain, api_url, archive_url].join
|
||||
|
|
@ -253,8 +269,8 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def raw
|
||||
domain = Gitea.gitea_config[:domain]
|
||||
api_url = Gitea.gitea_config[:base_url]
|
||||
domain = GiteaService.gitea_config[:domain]
|
||||
api_url = GiteaService.gitea_config[:base_url]
|
||||
|
||||
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}"
|
||||
file_path = [domain, api_url, url].join
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
class ReviewsController < ApplicationController
|
||||
before_action :require_login
|
||||
before_action :load_project
|
||||
before_action :load_pull_request
|
||||
|
||||
def create
|
||||
return render_forbidden('您不是审查人员,无法进行审查!') if current_user&.id != @pull_request.issue.assigned_to_id
|
||||
@review = Api::V1::Projects::Pulls::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
|
||||
end
|
||||
|
||||
private
|
||||
def review_params
|
||||
params.require(:review).permit(:content, :commit_id, :status)
|
||||
end
|
||||
|
||||
def load_pull_request
|
||||
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -2,24 +2,24 @@ class StatisticController < ApplicationController
|
|||
|
||||
# 平台概况
|
||||
def platform_profile
|
||||
@platform_user_query = Statistic::PlatformUserQuery.new(params).call
|
||||
@platform_project_query = Statistic::PlatformProjectQuery.new(params).call
|
||||
@platform_course_query = Statistic::PlatformCourseQuery.new(params).call
|
||||
@platform_user_query = Statistic::PlatformUserQuery.new(params).call rescue [0, 0, 0]
|
||||
@platform_project_query = Statistic::PlatformProjectQuery.new(params).call rescue [0, 0, 0]
|
||||
@platform_course_query = Statistic::PlatformCourseQuery.new(params).call rescue [0, 0, 0]
|
||||
end
|
||||
|
||||
# 平台代码提交数据
|
||||
def platform_code
|
||||
@platform_pull_request_query = Statistic::PlatformPullRequestQuery.new(params).call
|
||||
@platform_commit_query = Statistic::PlatformCommitQuery.new(params,current_user).call
|
||||
@platform_pull_request_query = Statistic::PlatformPullRequestQuery.new(params).call rescue [0, 0]
|
||||
@platform_commit_query = Statistic::PlatformCommitQuery.new(params,current_user).call rescue [0, 0]
|
||||
end
|
||||
|
||||
# 项目案例活跃度排行榜
|
||||
def active_project_rank
|
||||
@active_project_rank_query = Statistic::ActiveProjectRankQuery.new(params, current_user).call
|
||||
@active_project_rank_query = Statistic::ActiveProjectRankQuery.new(params, current_user).call rescue []
|
||||
end
|
||||
|
||||
# 开发者活跃度排行榜
|
||||
def active_developer_rank
|
||||
@active_developer_rank_query = Statistic::ActiveDeveloperRankQuery.new(params, current_user).call
|
||||
@active_developer_rank_query = Statistic::ActiveDeveloperRankQuery.new(params, current_user).call rescue []
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
class Traces::BaseController < ApplicationController
|
||||
|
||||
helper_method :observed_logged_user?, :observed_user
|
||||
before_action :check_trace_system
|
||||
|
||||
def observed_user
|
||||
@_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id]))
|
||||
end
|
||||
|
||||
def observed_logged_user?
|
||||
observed_user.id == User.current&.id
|
||||
end
|
||||
|
||||
protected
|
||||
def check_auth
|
||||
return render_forbidden unless current_user.admin? || observed_logged_user?
|
||||
end
|
||||
|
||||
def check_trace_system
|
||||
code, data, error = Trace::SystemInfoService.call(current_user.trace_token)
|
||||
return render_ok({code: 501, data: {operate_time: data['operate_time']}, message: '系统维护中'}) if data['status'] === 0
|
||||
rescue
|
||||
# 这里根据需求跳转到404
|
||||
return render_not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
class Users::IsPinnedProjectsController < Users::BaseController
|
||||
before_action :private_user_resources!, only: [:pin]
|
||||
def index
|
||||
@is_pinned_projects = observed_user.pinned_projects.order(position: :desc, created_at: :asc).includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
|
||||
def index
|
||||
@is_pinned_projects = observed_user.pinned_projects.left_joins(:project)
|
||||
.where("projects.is_public = TRUE")
|
||||
.order(position: :desc, created_at: :asc)
|
||||
.includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
|
||||
@is_pinned_projects = kaminari_paginate(@is_pinned_projects)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
title: Trustie API Reference
|
||||
title: GitLink API Reference
|
||||
|
||||
language_tabs: # must be one of https://git.io/vQNgJ
|
||||
- shell: Shell
|
||||
- javascript: JavaScript
|
||||
|
||||
toc_footers:
|
||||
- <a href='https://www.trustie.net/login?login=false'>Sign Up for a User</a>
|
||||
- <a href='https://www.trustie.net'>Powered by Trustie</a>
|
||||
- <a href='https://www.gitlink.org.cn/login'>Sign In for a User</a>
|
||||
- <a href='https://www.gitlink.org.cn'>Powered by GitLink</a>
|
||||
|
||||
includes:
|
||||
- licenses
|
||||
|
|
@ -30,8 +30,8 @@ code_clipboard: true
|
|||
|
||||
# Introduction
|
||||
|
||||
Welcome to the Trustie API! You can use our API to access Trustie API endpoints, which can get information on projects, repository, and users in our platform.
|
||||
Welcome to the GitLink API! You can use our API to access GitLink API endpoints, which can get information on projects, repository, and users in our platform.
|
||||
|
||||
We have language bindings in Shell,avaScript! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.
|
||||
|
||||
This example API documentation page was created with [Trustie](https://www.trustie.net). Feel free to edit it and use it as a base for your own API's documentation.
|
||||
This example API documentation page was created with [GitLink](https://www.gitlink.org.cn). Feel free to edit it and use it as a base for your own API's documentation.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,278 @@
|
|||
# Projects
|
||||
## 获取项目邀请链接(项目管理员)
|
||||
当前登录(管理员)用户获取项目邀请链接的接口(第一次请求会默认生成role类型为developer和is_apply为true的链接)
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/current_link.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/yystopf/kellect/project_invite_links/current_link.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET /api/:owner/:repo/project_invite_links/current_link.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|role |是| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 |
|
||||
|is_apply |是| |boolean |是否需要审核 |
|
||||
|
||||
### 返回字段说明
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|id |int |链接id |
|
||||
|role |string |邀请角色|
|
||||
|is_apply |boolean |是否需要审核 |
|
||||
|sign |string |邀请标识(放在链接后面即可)|
|
||||
|expired_at |string |链接过期时间|
|
||||
|user.id |int |链接创建者的id |
|
||||
|user.type |string |链接创建者的类型 |
|
||||
|user.name |string |链接创建者的名称 |
|
||||
|user.login |string |链接创建者的标识 |
|
||||
|user.image_url |string |链接创建者头像 |
|
||||
|project.id |int |链接关联项目的id |
|
||||
|project.identifier |string |链接关联项目的标识 |
|
||||
|project.name |string |链接关联项目的名称 |
|
||||
|project.description |string |链接关联项目的描述 |
|
||||
|project.is_public |bool |链接关联项目是否公开 |
|
||||
|project.owner.id |bool |链接关联项目拥有者id |
|
||||
|project.owner.type |string |链接关联项目拥有者类型 |
|
||||
|project.owner.name |string |链接关联项目拥有者昵称 |
|
||||
|project.owner.login |string |链接关联项目拥有者标识 |
|
||||
|project.owner.image_url|string |链接关联项目拥有者头像 |
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 7,
|
||||
"role": "developer",
|
||||
"is_apply": false,
|
||||
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
|
||||
"expired_at": "2022-06-23 10:08",
|
||||
"user": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
},
|
||||
"project": {
|
||||
"id": 474,
|
||||
"identifier": "kellect",
|
||||
"name": "kellect",
|
||||
"description": null,
|
||||
"is_public": true,
|
||||
"owner": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
## 生成项目邀请链接(项目管理员)
|
||||
当前登录(管理员)用户生成的项目邀请链接,可选role和is_apply参数
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/generate_link.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/yystopf/kellect/project_invite_links/generate_link.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/:owner/:repo/project_invite_links/generate_link.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|role |是| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 |
|
||||
|is_apply |是| |boolean |是否需要审核 |
|
||||
|
||||
|
||||
> 请求的JSON示例
|
||||
|
||||
```json
|
||||
{
|
||||
"role": "developer",
|
||||
"is_apply": false
|
||||
}
|
||||
```
|
||||
|
||||
### 返回字段说明
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|id |int |链接id |
|
||||
|role |string |邀请角色|
|
||||
|is_apply |boolean |是否需要审核 |
|
||||
|sign |string |邀请标识(放在链接后面即可)|
|
||||
|expired_at |string |链接过期时间|
|
||||
|user.id |int |链接创建者的id |
|
||||
|user.type |string |链接创建者的类型 |
|
||||
|user.name |string |链接创建者的名称 |
|
||||
|user.login |string |链接创建者的标识 |
|
||||
|user.image_url |string |链接创建者头像 |
|
||||
|project.id |int |链接关联项目的id |
|
||||
|project.identifier |string |链接关联项目的标识 |
|
||||
|project.name |string |链接关联项目的名称 |
|
||||
|project.description |string |链接关联项目的描述 |
|
||||
|project.is_public |bool |链接关联项目是否公开 |
|
||||
|project.owner.id |bool |链接关联项目拥有者id |
|
||||
|project.owner.type |string |链接关联项目拥有者类型 |
|
||||
|project.owner.name |string |链接关联项目拥有者昵称 |
|
||||
|project.owner.login |string |链接关联项目拥有者标识 |
|
||||
|project.owner.image_url|string |链接关联项目拥有者头像 |
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 7,
|
||||
"role": "developer",
|
||||
"is_apply": false,
|
||||
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
|
||||
"expired_at": "2022-06-23 10:08",
|
||||
"user": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
},
|
||||
"project": {
|
||||
"id": 474,
|
||||
"identifier": "kellect",
|
||||
"name": "kellect",
|
||||
"description": null,
|
||||
"is_public": true,
|
||||
"owner": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 获取邀请链接信息(被邀请用户)
|
||||
用户请求邀请链接时,通过该接口来获取链接的信息
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET http://localhost:3000/api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/yystopf/kellect/project_invite_links/show_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/:owner/:repo/project_invite_links/show_link.json?invite_sign=xxx`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|invite_sign |是| |string |项目邀请链接的标识 |
|
||||
|
||||
### 返回字段说明
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|id |int |链接id |
|
||||
|role |string |邀请角色|
|
||||
|is_apply |boolean |是否需要审核 |
|
||||
|sign |string |邀请标识(放在链接后面即可)|
|
||||
|expired_at |string |链接过期时间|
|
||||
|user.id |int |链接创建者的id |
|
||||
|user.type |string |链接创建者的类型 |
|
||||
|user.name |string |链接创建者的名称 |
|
||||
|user.login |string |链接创建者的标识 |
|
||||
|user.image_url |string |链接创建者头像 |
|
||||
|project.id |int |链接关联项目的id |
|
||||
|project.identifier |string |链接关联项目的标识 |
|
||||
|project.name |string |链接关联项目的名称 |
|
||||
|project.description |string |链接关联项目的描述 |
|
||||
|project.is_public |bool |链接关联项目是否公开 |
|
||||
|project.owner.id |bool |链接关联项目拥有者id |
|
||||
|project.owner.type |string |链接关联项目拥有者类型 |
|
||||
|project.owner.name |string |链接关联项目拥有者昵称 |
|
||||
|project.owner.login |string |链接关联项目拥有者标识 |
|
||||
|project.owner.image_url|string |链接关联项目拥有者头像 |
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 7,
|
||||
"role": "developer",
|
||||
"is_apply": false,
|
||||
"sign": "6b6b454843c291d4e52e60853cb8ad9f",
|
||||
"expired_at": "2022-06-23 10:08",
|
||||
"user": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
},
|
||||
"project": {
|
||||
"id": 474,
|
||||
"identifier": "kellect",
|
||||
"name": "kellect",
|
||||
"description": null,
|
||||
"is_public": true,
|
||||
"owner": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 接受项目邀请链接(被邀请用户)
|
||||
当前登录(非项目)用户加入项目的接口,如果项目链接不需要审核,请求成功后即加入项目,如果需要审核,那么会提交一个申请,需要项目管理员审核
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/yystopf/kellect/project_invite_links/redirect_link.json?invite_sign=d612df03aad63760445c187bcf83f2e6')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/:owner/:repo/project_invite_links/redirect_link.json?invite_sign=xxx`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|invite_sign |是| |string |项目邀请链接的标识 |
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
## 申请加入项目
|
||||
申请加入项目
|
||||
|
|
@ -849,4 +1123,36 @@ await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.j
|
|||
"created_at": "2021-04-26 09:54",
|
||||
"time_ago": "1分钟前"
|
||||
}
|
||||
```
|
||||
|
||||
## 退出项目
|
||||
供项目成员(开发者、报告者)退出项目用
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/quit.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/:owner/:repo/quit.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/:owner/:repo/quit.json`
|
||||
|
||||
### 请求参数
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|owner |是| |string |用户登录名 |
|
||||
|repo |是| |string |项目标识identifier |
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1,78 @@
|
|||
# Teams
|
||||
|
||||
## 团队下新增所有的项目
|
||||
团队下新增所有的项目
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST \
|
||||
http://localhost:3000/api/organizations/ceshi_org/teams/28/team_projects/create_all
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/organizations/ceshi_org/teams/28/team_projects/create_all.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/organizations/:organization/teams/:id/team_projects/create_all.json`
|
||||
|
||||
### 请求参数:
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|organization |是| | string |组织标识 |
|
||||
|id |是| | integer|团队 ID|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success Data.
|
||||
</aside>
|
||||
|
||||
|
||||
## 团队下删除所有的项目
|
||||
团队下删除所有的项目
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X DELETE \
|
||||
http://localhost:3000/api/organizations/ceshi_org/teams/28/team_projects/destroy_all
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('DELETE /api/organizations/ceshi_org/teams/28/team_projects/destroy_all.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`DELETE /api/organizations/:organization/teams/:id/team_projects/destroy_all.json`
|
||||
|
||||
### 请求参数:
|
||||
参数 | 必选 | 默认 | 类型 | 字段说明
|
||||
--------- | ------- | ------- | -------- | ----------
|
||||
|organization |是| | string |组织标识 |
|
||||
|id |是| | integer|团队 ID|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
<aside class="success">
|
||||
Success Data.
|
||||
</aside>
|
||||
|
|
@ -47,6 +47,105 @@ await octokit.request('GET /api/users/me.json')
|
|||
Success Data.
|
||||
</aside>
|
||||
|
||||
## 用户项目列表
|
||||
获取用户项目列表
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET http://localhost:3000/api/v1/:login/projects.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/v1/:login/projects.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET api/v1/yystopf/projects.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login | string | 用户的用户名|
|
||||
|category | string | 归属项目,默认为全部项目, join: 参与项目, created: 创建项目, manage: 管理项目, watched: 关注项目, forked: 复刻项目 |
|
||||
|is_public | boolean | 公/私有项目,true为公开项目,false为私有项目|
|
||||
|project_type | string | 项目类型,common为托管项目, mirror为镜像项目, sync_mirror为同步镜像项目|
|
||||
|sort_by | string | 项目的排序字段,比如 created_on, updated_on等|
|
||||
|sort_direction | string | 排序的类型,desc 倒序,asc 正序|
|
||||
|limit | integer | 每页个数 |
|
||||
|page | integer | 页码 |
|
||||
|
||||
### 返回字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|total_count | integer | 项目总数 |
|
||||
|projects.owner.id | integer | 项目拥有者id |
|
||||
|projects.owner.type | string | 项目拥有者类型,User 用户,Organization 组织 |
|
||||
|projects.owner.name | string | 项目拥有者名称|
|
||||
|projects.owner.login | string | 项目拥有者用户名 |
|
||||
|projects.owner.image_url | string | 项目拥有者头像地址 |
|
||||
|type | string | 项目类型,common 托管项目,mirror 镜像项目, sync 同步镜像项目 |
|
||||
|description | string | 项目描述 |
|
||||
|forked_count | integer | 项目复刻数量 |
|
||||
|forked_from_project_id | integer | 项目复刻来源项目 |
|
||||
|identifier | string | 项目标识 |
|
||||
|issues_count | integer | 项目issues数量|
|
||||
|pull_requests_count | integer | 项目合并请求数量 |
|
||||
|invite_code | string | 项目邀请码|
|
||||
|website | string | 项目网址|
|
||||
|platform | string | 项目平台 |
|
||||
|name | string | 项目名称|
|
||||
|open_devops | boolean | 项目是否开启工作流 |
|
||||
|praises_count | integer | 项目点赞数量|
|
||||
|is_public | boolean | 项目是否公开|
|
||||
|status | integer | 项目状态|
|
||||
|watchers_count | integer | 项目关注数量|
|
||||
|ignore_id | integer | 项目ignoreID|
|
||||
|license_id | integer | 项目许可证ID|
|
||||
|project_category_id | integer | 项目分类ID|
|
||||
|project_language_id | integer | 项目语言ID|
|
||||
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
"total_count": 1,
|
||||
"projects": [
|
||||
{
|
||||
"owner": {
|
||||
"id": 2,
|
||||
"type": "User",
|
||||
"name": "heh",
|
||||
"login": "yystopf",
|
||||
"image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
|
||||
},
|
||||
"type": "common",
|
||||
"description": null,
|
||||
"forked_count": 1,
|
||||
"forked_from_project_id": null,
|
||||
"identifier": "hahahah",
|
||||
"issues_count": 5,
|
||||
"pull_requests_count": 3,
|
||||
"invite_code": "8zfKtM",
|
||||
"website": null,
|
||||
"platform": "forge",
|
||||
"name": "hahahah",
|
||||
"open_devops": false,
|
||||
"praises_count": 0,
|
||||
"is_public": true,
|
||||
"status": 1,
|
||||
"watchers_count": 0,
|
||||
"ignore_id": null,
|
||||
"license_id": null,
|
||||
"project_category_id": null,
|
||||
"project_language_id": null
|
||||
}
|
||||
]
|
||||
```
|
||||
<aside class="success">
|
||||
Success Data.
|
||||
</aside>
|
||||
|
||||
## 用户消息列表
|
||||
获取用户消息列表
|
||||
|
||||
|
|
@ -2205,4 +2304,189 @@ await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
|
|||
"created_at": "2021-06-09 16:41",
|
||||
"time_ago": "7分钟前"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 用户发送邮件验证码
|
||||
用户发送邮件验证码
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X GET http://localhost:3000/api/v1/yystopf/send_email_vefify_code.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('GET /api/v1/:login/send_email_vefify_code.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`GET /api/v1/:login/send_email_vefify_code.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login |string |用户标识 |
|
||||
|code_type |int |10: 更新邮箱|
|
||||
|email |string |邮箱|
|
||||
|smscode |string |邮箱md5加密值|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 用户验证邮件验证码
|
||||
用户验证邮件验证码
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/v1/yystopf/check_email_verify_code.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/v1/:login/check_email_verify_code.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/v1/:login/check_email_verify_code.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login |string |用户标识 |
|
||||
|code_type |int |10: 更新邮箱|
|
||||
|email |string |邮箱|
|
||||
|code |string |邮箱验证码|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 用户验证密码
|
||||
用户验证密码,检查是否和用户密码一致
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/v1/yystopf/check_password.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/v1/:login/check_password.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/v1/:login/check_password.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login |string |用户标识 |
|
||||
|password |string |用户密码|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 用户验证邮箱
|
||||
用户验证邮箱是否符合规范以及是否已被使用
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X POST http://localhost:3000/api/v1/yystopf/check_email.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('POST /api/v1/:login/check_email.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`POST /api/v1/:login/check_email.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login |string |用户标识 |
|
||||
|email |string |邮箱地址|
|
||||
|
||||
### 返回字段说明:
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 用户更改邮箱
|
||||
用户更改一个新的邮箱
|
||||
|
||||
> 示例:
|
||||
|
||||
```shell
|
||||
curl -X PATCH http://localhost:3000/api/v1/yystopf/update_email.json
|
||||
```
|
||||
|
||||
```javascript
|
||||
await octokit.request('PATCH /api/v1/:login/update_email.json')
|
||||
```
|
||||
|
||||
### HTTP 请求
|
||||
`PATCH /api/v1/:login/update_email.json`
|
||||
|
||||
### 请求字段说明:
|
||||
参数 | 类型 | 字段说明
|
||||
--------- | ----------- | -----------
|
||||
|login |string |用户标识 |
|
||||
|password |string |用户密码|
|
||||
|email |string |邮箱地址|
|
||||
|code |string |邮箱验证码|
|
||||
|
||||
|
||||
> 请求的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"password": "Aa19960425.",
|
||||
"code": "657134",
|
||||
"email": "yystopf@163.com"
|
||||
}
|
||||
```
|
||||
|
||||
> 返回的JSON示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success"
|
||||
}
|
||||
```
|
||||
|
|
@ -3,6 +3,8 @@ class Contents::CreateForm < BaseForm
|
|||
|
||||
validates :filepath, presence: true
|
||||
|
||||
validates :new_branch, length: { maximum: 100, too_long: "过长,仅支持%{count}的长度"}
|
||||
|
||||
validate :check_branch
|
||||
|
||||
def check_branch
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
class Projects::ProjectInviteLinks::CreateForm < BaseForm
|
||||
attr_accessor :user_id, :project_id, :role, :is_apply
|
||||
|
||||
validates :user_id, :project_id, :role, presence: true
|
||||
validates :role, inclusion: { in: %w(manager developer reporter), message: "请输入正确的权限." }
|
||||
validates :is_apply, inclusion: {in: [true, false], message: "请输入是否需要管理员审核."}
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
module Admins::NpsHelper
|
||||
end
|
||||
|
|
@ -30,7 +30,7 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def gitea_domain
|
||||
Gitea.gitea_config[:domain]
|
||||
GiteaService.gitea_config[:domain]
|
||||
end
|
||||
|
||||
def find_user_by_login_or_mail(identifier)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ module RepositoriesHelper
|
|||
end
|
||||
|
||||
def download_type(str)
|
||||
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
|
||||
default_type = %w(ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv apk)
|
||||
default_type.include?(str&.downcase) || str.blank?
|
||||
end
|
||||
|
||||
|
|
@ -104,8 +104,7 @@ module RepositoriesHelper
|
|||
# 链接直接跳过不做替换
|
||||
next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank?
|
||||
ext = File.extname(s_content)[1..-1]
|
||||
|
||||
if image_type?(ext) || download_type(ext)
|
||||
if (image_type?(ext) || download_type(ext)) && !ext.blank?
|
||||
s_content = File.expand_path(s_content, file_path)
|
||||
s_content = s_content.split("#{Rails.root}/")[1]
|
||||
# content = content.gsub(s[0], "/#{s_content}")
|
||||
|
|
@ -115,7 +114,7 @@ module RepositoriesHelper
|
|||
path = [owner&.login, repo&.identifier, 'tree', ref, file_path].join("/")
|
||||
s_content = File.expand_path(s_content, path)
|
||||
s_content = s_content.split("#{Rails.root}/")[1]
|
||||
content = content.gsub(s[0], "/#{s_content}")
|
||||
content = content.gsub('('+s[0]+')', '('+"/#{s_content}"+')')
|
||||
end
|
||||
rescue
|
||||
next
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ module TagChosenHelper
|
|||
cache_key = "project-#{project.id}/all_milestones/size-#{project.versions.size}/#{project.versions.maximum('updated_on')}"
|
||||
|
||||
Rails.cache.fetch(cache_key) do
|
||||
project.versions.select(:id, :name, :status).collect do |event|
|
||||
project.versions.opening.select(:id, :name, :status).collect do |event|
|
||||
{
|
||||
id: event.id,
|
||||
name: event.name,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ module Gitea
|
|||
file_params = {}
|
||||
file_params = file_params.merge(branch: @params[:branch]) unless @params[:branch].blank?
|
||||
file_params = file_params.merge(new_branch: @params[:new_branch]) unless @params[:new_branch].blank?
|
||||
file_params = file_params.merge(content: Base64.encode64(@params[:content] || ""))
|
||||
file_params = file_params.merge(content: @params[:content] || "")
|
||||
file_params = file_params.merge(message: @params[:message]) unless @params[:message].blank?
|
||||
file_params = file_params.merge(committer: @params[:committer])
|
||||
file_params
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ module Gitea
|
|||
|
||||
def token
|
||||
{
|
||||
username: Gitea.gitea_config[:access_key_id],
|
||||
password: Gitea.gitea_config[:access_key_secret]
|
||||
username: GiteaService.gitea_config[:access_key_id],
|
||||
password: GiteaService.gitea_config[:access_key_secret]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,24 @@ class SendTemplateMessageJob < ApplicationJob
|
|||
def perform(source, *args)
|
||||
Rails.logger.info "SendTemplateMessageJob [args] #{args}"
|
||||
case source
|
||||
when 'CustomTip'
|
||||
receivers_id, template_id, props = args[0], args[1], args[2]
|
||||
template = MessageTemplate.find_by_id(template_id)
|
||||
return unless template.present?
|
||||
receivers = User.where(id: receivers_id).or(User.where(mail: receivers_id))
|
||||
not_exists_receivers = receivers_id - receivers.pluck(:id) - receivers.pluck(:mail)
|
||||
receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props)
|
||||
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props})
|
||||
receivers.find_each do |receiver|
|
||||
receivers_email_string, email_title, email_content = MessageTemplate::CustomTip.get_email_message_content(receiver, template, props)
|
||||
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
|
||||
end
|
||||
not_exists_receivers.each do |mail|
|
||||
valid_email_regex = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/i
|
||||
next unless (mail =~ valid_email_regex)
|
||||
email_title, email_content = MessageTemplate::CustomTip.get_email_content(template, props)
|
||||
Notice::Write::EmailCreateService.call(mail, email_title, email_content)
|
||||
end
|
||||
when 'FollowTip'
|
||||
watcher_id = args[0]
|
||||
watcher = Watcher.find_by_id(watcher_id)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Ci::Drone::Server
|
|||
|
||||
private
|
||||
def gitea_url
|
||||
Gitea.gitea_config[:domain]
|
||||
GiteaService.gitea_config[:domain]
|
||||
end
|
||||
|
||||
def database_username
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
module CustomRegexp
|
||||
PHONE = /1\d{10}/
|
||||
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
|
||||
EMAIL = /\A[a-zA-Z0-9]+([._\-\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
|
||||
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}\z/ #只含有数字、字母、下划线不能以下划线开头和结尾
|
||||
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
|
||||
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module Gitea
|
||||
module GiteaService
|
||||
class << self
|
||||
def gitea_config
|
||||
gitea_config = {}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
class UserMailer < ApplicationMailer
|
||||
# 注意:这个地方一定要和你的邮箱服务域名一致
|
||||
default from: 'notification@trustie.org'
|
||||
default from: 'admin@trustie.org'
|
||||
|
||||
# 用户注册验证码
|
||||
def register_email(mail, code)
|
||||
|
|
@ -8,4 +8,12 @@ class UserMailer < ApplicationMailer
|
|||
mail(to: mail, subject: 'Gitink | 注册验证码')
|
||||
end
|
||||
|
||||
def update_email(mail, code)
|
||||
@code = code
|
||||
mail(to: mail, subject: 'Gitink | 更改邮箱验证码')
|
||||
end
|
||||
|
||||
def feedback_email(mail, title, content)
|
||||
mail(to: mail, subject: title, content_type: "text/html", body: content)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,24 +2,27 @@
|
|||
#
|
||||
# Table name: forge_applied_projects
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# project_id :integer
|
||||
# user_id :integer
|
||||
# role :integer default("0")
|
||||
# status :integer default("0")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# id :integer not null, primary key
|
||||
# project_id :integer
|
||||
# user_id :integer
|
||||
# role :integer default("0")
|
||||
# status :integer default("0")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# project_invite_link_id :integer
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_forge_applied_projects_on_project_id (project_id)
|
||||
# index_forge_applied_projects_on_user_id (user_id)
|
||||
# index_forge_applied_projects_on_project_id (project_id)
|
||||
# index_forge_applied_projects_on_project_invite_link_id (project_invite_link_id)
|
||||
# index_forge_applied_projects_on_user_id (user_id)
|
||||
#
|
||||
|
||||
class AppliedProject < ApplicationRecord
|
||||
self.table_name = "forge_applied_projects"
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :project_invite_link, optional: true
|
||||
|
||||
has_many :applied_messages, as: :applied, dependent: :destroy
|
||||
# has_many :forge_activities, as: :forge_act, dependent: :destroy
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
class CommitLog < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :repository
|
||||
|
||||
end
|
||||
|
|
@ -17,30 +17,69 @@ module ProjectOperable
|
|||
owner.build_permit_team_projects!(id)
|
||||
# 避免自己创建的项目,却无法拥有访问权,因为该用户所在团队暂未获得项目访问权
|
||||
return if creator.nil? || owner.is_owner?(creator.id)
|
||||
add_member!(creator.id, "Manager")
|
||||
add_member!(creator.id, "Manager") if creator.is_a?(User)
|
||||
end
|
||||
|
||||
def add_member!(user_id, role_name='Developer')
|
||||
if self.owner.is_a?(Organization)
|
||||
case role_name
|
||||
when 'Manager'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.admin.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 新增对应的团队成员
|
||||
team_user = TeamUser.build(self.user_id, user_id, team.id)
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
|
||||
# 确保组织成员中有该用户
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
when 'Developer'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.write.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 新增对应的团队成员
|
||||
team_user = TeamUser.build(self.user_id, user_id, team.id)
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
|
||||
# 确保组织成员中有该用户
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
when 'Reporter'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.read.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 新增对应的团队成员
|
||||
team_user = TeamUser.build(self.user_id, user_id, team.id)
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
|
||||
# 确保组织成员中有该用户
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
end
|
||||
end
|
||||
member = members.create!(user_id: user_id, team_user_id: team_user&.id)
|
||||
|
|
@ -71,26 +110,70 @@ module ProjectOperable
|
|||
|
||||
def change_member_role!(user_id, role)
|
||||
member = self.member(user_id)
|
||||
# 所有者为组织,并且该用户属于组织成员
|
||||
if self.owner.is_a?(Organization) && member.team_user.present?
|
||||
case role&.name
|
||||
when 'Manager'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.admin.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 更改对应的团队成员
|
||||
team_user = member.team_user
|
||||
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
|
||||
|
||||
# 确保组织成员中有该用户
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
team_user = member.team_user.update(team_id: team&.id)
|
||||
when 'Developer'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.write.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
$gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 更改对应的团队成员
|
||||
team_user = member.team_user
|
||||
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
|
||||
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
team_user = member.team_user.update(team_id: team&.id)
|
||||
when 'Reporter'
|
||||
# 构建相应的团队
|
||||
team = self.owner.teams.read.take
|
||||
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
|
||||
TeamProject.build(self.user_id, team.id, self.id)
|
||||
if team.nil?
|
||||
team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false)
|
||||
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
|
||||
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
|
||||
end
|
||||
|
||||
# 设置项目在团队中的访问权限
|
||||
team_project = TeamProject.build(self.user_id, team.id, self.id)
|
||||
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
|
||||
|
||||
# 更改对应的团队成员
|
||||
team_user = member.team_user
|
||||
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
|
||||
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
|
||||
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
|
||||
|
||||
# 确保组织成员中有该用户
|
||||
OrganizationUser.build(self.user_id, user_id)
|
||||
team_user = member.team_user.update(team_id: team&.id)
|
||||
end
|
||||
end
|
||||
member.member_roles.last.update_attributes!(role: role)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: feedbacks
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# user_id :integer
|
||||
# content :text(65535)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_feedbacks_on_user_id (user_id)
|
||||
#
|
||||
|
||||
class Feedback < ApplicationRecord
|
||||
|
||||
belongs_to :user
|
||||
has_many :feedback_message_histories, dependent: :destroy
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: feedback_message_histories
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# feedback_id :integer
|
||||
# user_id :integer
|
||||
# title :string(255)
|
||||
# content :text(65535)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_feedback_message_histories_on_feedback_id (feedback_id)
|
||||
# index_feedback_message_histories_on_user_id (user_id)
|
||||
#
|
||||
|
||||
class FeedbackMessageHistory < ApplicationRecord
|
||||
|
||||
belongs_to :feedback
|
||||
belongs_to :user
|
||||
|
||||
before_validation :send_meessage_email, on: :create
|
||||
|
||||
private
|
||||
|
||||
def send_meessage_email
|
||||
unless UserMailer.feedback_email(feedback&.user&.mail, title, content).deliver_now
|
||||
errors[:title] << '邮件发送失败!'
|
||||
end
|
||||
rescue
|
||||
errors[:title] << '邮件发送失败!'
|
||||
end
|
||||
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue