# Conflicts:
#	app/controllers/application_controller.rb
#	app/controllers/pull_requests_controller.rb
#	app/controllers/users_controller.rb
#	app/models/ci/user.rb
#	app/models/gitea/pull.rb
#	app/models/gitea/webhook.rb
#	app/models/gitea/webhook_task.rb
#	app/models/organization.rb
#	app/models/project.rb
#	app/models/project_category.rb
#	app/models/user.rb
#	app/services/projects/create_service.rb
This commit is contained in:
xxq250 2023-01-12 17:51:48 +08:00
commit fc4db3bb9a
714 changed files with 64682 additions and 1253939 deletions

6
.gitignore vendored
View File

@ -73,7 +73,7 @@ vendor/bundle/
/public/admin /public/admin
/mysql_data /mysql_data
/public/repo/ /public/repo/
/coverage
.generators .generators
.rakeTasks .rakeTasks
@ -81,7 +81,7 @@ db/bak/
docker/ docker/
educoder.sql educoder.sql
redis_data/ redis_data/
Dockerfile
dump.rdb dump.rdb
.tags* .tags*
ceshi_user.xlsx ceshi_user.xlsx
public/trace_task_results

33
Dockerfile Normal file
View File

@ -0,0 +1,33 @@
FROM ubuntu:18.04
RUN apt update
RUN apt install -y openssl libssl-dev imagemagick git ruby-dev nodejs libmariadb-dev libmysqlclient-dev shared-mime-info libpq-dev libxml2-dev libxslt-dev
RUN DEBIAN_FRONTEND="noninteractive" apt -y install tzdata
RUN ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /home/app/gitlink
ADD ./ /home/app/gitlink
RUN gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
RUN gem update --system
RUN gem install bundler
RUN gem install rake
RUN rm -rf Gemfile.lock
#RUN cp config/configuration.yml.example config/configuration.yml
#RUN cp config/database.yml.example config/database.yml
#RUN touch config/redis.yml
#RUN touch config/elasticsearch.yml
RUN bundle install
EXPOSE 4000
RUN rails s -p 4000 -b '0.0.0.0'

278
Gemfile
View File

@ -1,130 +1,148 @@
source 'https://gems.ruby-china.com' source 'https://gems.ruby-china.com'
git_source(:github) { |repo| "https://github.com/#{repo}.git" } git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', '~> 5.2.0' gem 'rails', '~> 5.2.0'
gem 'mysql2', '>= 0.4.4', '< 0.6.0' gem 'mysql2', '>= 0.4.4', '< 0.6.0'
gem 'puma', '~> 3.11' gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5.0' gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0' gem 'uglifier', '>= 1.3.0'
# gem 'coffee-rails', '~> 4.2' # gem 'coffee-rails', '~> 4.2'
gem 'turbolinks', '~> 5' gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5' gem 'jbuilder', '~> 2.5'
gem 'groupdate', '~> 4.1.0' gem 'groupdate', '~> 4.1.0'
gem 'chartkick' gem 'chartkick'
gem 'grape-entity', '~> 0.7.1' gem 'grape-entity', '~> 0.7.1'
gem 'kaminari', '~> 1.1', '>= 1.1.1' gem 'kaminari', '~> 1.1', '>= 1.1.1'
gem 'bootsnap', '>= 1.1.0', require: false gem 'bootsnap', '>= 1.1.0', require: false
gem 'chinese_pinyin' gem 'chinese_pinyin'
gem 'rack-cors' gem 'rack-cors'
gem 'redis-rails' gem 'redis-rails'
gem 'roo-xls' gem 'roo-xls'
gem 'simple_xlsx_reader' gem 'simple_xlsx_reader'
gem 'rubyzip' gem 'rubyzip'
gem 'spreadsheet' gem 'spreadsheet'
gem 'ruby-ole' gem 'ruby-ole'
# 导出为xlsx # 导出为xlsx
gem 'axlsx', '~> 3.0.0.pre' gem 'axlsx', '~> 3.0.0.pre'
gem 'axlsx_rails', '~> 0.5.2' gem 'axlsx_rails', '~> 0.5.2'
gem 'oauth2' gem 'oauth2'
#导出为pdf #导出为pdf
gem 'pdfkit' gem 'pdfkit'
gem 'wkhtmltopdf-binary' gem 'wkhtmltopdf-binary'
# gem 'request_store' # gem 'request_store'
#gem 'iconv' #gem 'iconv'
# markdown 转html # markdown 转html
gem 'redcarpet', '~> 3.4' gem 'redcarpet', '~> 3.4'
gem 'rqrcode', '~> 0.10.1' gem 'rqrcode', '~> 0.10.1'
gem 'rqrcode_png' gem 'rqrcode_png'
gem 'acts-as-taggable-on', '~> 6.0' gem 'acts-as-taggable-on', '~> 6.0'
# a tree structure # a tree structure
gem 'ancestry' gem 'ancestry'
gem 'acts_as_list' gem 'acts_as_list'
gem 'omniauth-cas' gem 'omniauth-cas'
# profiler Middleware # profiler Middleware
gem 'rack-mini-profiler' gem 'rack-mini-profiler'
# object-based searching # object-based searching
gem 'ransack' gem 'ransack'
group :development, :test do group :development, :test do
gem 'rspec-rails', '~> 3.8' gem 'rspec-rails', '~> 3.8'
end gem 'rails-controller-testing'
end
group :development do
gem 'prettier' group :development do
gem 'rubocop', '~> 0.52.0' gem 'prettier'
gem 'solargraph', '~> 0.38.0' gem 'rubocop', '~> 0.52.0'
gem 'awesome_print' gem 'solargraph', '~> 0.38.0'
gem 'web-console', '>= 3.3.0' gem 'awesome_print'
gem 'listen', '>= 3.0.5', '< 3.2' gem 'web-console', '>= 3.3.0'
gem 'spring' gem 'listen', '>= 3.0.5', '< 3.2'
gem 'spring-watcher-listen', '~> 2.0.0' gem 'spring'
gem "annotate", "~> 2.6.0" gem 'spring-watcher-listen', '~> 2.0.0'
end gem "annotate", "~> 2.6.0"
end
group :test do
gem 'capybara', '>= 2.15', '< 4.0' group :test do
gem 'selenium-webdriver' gem 'capybara', '>= 2.15', '< 4.0'
gem 'chromedriver-helper' gem 'selenium-webdriver'
end gem 'chromedriver-helper'
gem 'simplecov', '~>0.12.0', require: false
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] end
#编码检测 gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'rchardet', '~> 1.8'
#编码检测
# http client gem 'rchardet', '~> 1.8'
gem 'faraday', '~> 0.15.4'
# http client
# view gem 'faraday', '~> 0.15.4'
gem 'active_decorator'
gem 'bootstrap', '~> 4.3.1' # view
gem 'jquery-rails' gem 'active_decorator'
gem 'simple_form' gem 'bootstrap', '~> 4.3.1'
gem 'font-awesome-sass', '4.7.0' gem 'jquery-rails'
gem 'simple_form'
# i18n gem 'font-awesome-sass', '4.7.0'
gem 'rails-i18n', '~> 5.1'
# i18n
# job gem 'rails-i18n', '~> 5.1'
gem 'sidekiq'
gem 'sinatra' # job
gem "sidekiq-cron", "~> 1.1" gem 'sidekiq',"5.2.8"
gem 'sinatra'
# batch insert gem "sidekiq-cron", "1.2.0"
gem 'bulk_insert' gem 'whenever'
# elasticsearch # batch insert
gem 'searchkick' gem 'bulk_insert'
gem 'aasm' # elasticsearch
gem 'enumerize' gem 'searchkick'
gem 'diffy' gem 'aasm'
gem 'enumerize'
gem 'deep_cloneable', '~> 3.0.0'
gem 'diffy'
# oauth2
gem 'omniauth', '~> 1.9.0' gem 'deep_cloneable', '~> 3.0.0'
gem 'omniauth-oauth2', '~> 1.6.0'
# oauth2
# global var gem 'omniauth', '~> 1.9.0'
gem 'request_store' gem 'omniauth-oauth2', '~> 1.6.0'
gem "omniauth-github"
# 敏感词汇 gem "omniauth-rails_csrf_protection"
gem 'harmonious_dictionary', '~> 0.0.1' gem 'omniauth-gitee', '~> 1.0.0'
gem "omniauth-wechat-oauth2"
gem 'parallel', '~> 1.19', '>= 1.19.1'
# global var
gem 'letter_avatar' gem 'request_store'
# 敏感词汇
gem 'harmonious_dictionary', '~> 0.0.1'
gem 'parallel', '~> 1.19', '>= 1.19.1'
# log
gem 'multi_logger'
gem 'letter_avatar'
gem 'jwt'
gem 'doorkeeper'
gem 'doorkeeper-jwt'
gem 'gitea-client', '~> 0.11.1'

View File

@ -106,6 +106,12 @@ GEM
activerecord (>= 3.1.0, < 7) activerecord (>= 3.1.0, < 7)
diff-lcs (1.3) diff-lcs (1.3)
diffy (3.3.0) diffy (3.3.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.5.1)
railties (>= 5)
doorkeeper-jwt (0.4.1)
jwt (>= 2.1)
e2mmap (0.1.0) e2mmap (0.1.0)
elasticsearch (7.5.0) elasticsearch (7.5.0)
elasticsearch-api (= 7.5.0) elasticsearch-api (= 7.5.0)
@ -129,6 +135,8 @@ GEM
fugit (1.4.1) fugit (1.4.1)
et-orbi (~> 1.1, >= 1.1.8) et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4) raabro (~> 1.4)
gitea-client (0.10.5)
rest-client (~> 2.1.0)
globalid (0.4.2) globalid (0.4.2)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
grape-entity (0.7.1) grape-entity (0.7.1)
@ -139,6 +147,9 @@ GEM
harmonious_dictionary (0.0.1) harmonious_dictionary (0.0.1)
hashie (3.6.0) hashie (3.6.0)
htmlentities (4.3.4) htmlentities (4.3.4)
http-accept (1.7.0)
http-cookie (1.0.5)
domain_name (~> 0.5)
i18n (1.8.2) i18n (1.8.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
io-like (0.3.1) io-like (0.3.1)
@ -176,6 +187,9 @@ GEM
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
maruku (0.7.3) maruku (0.7.3)
method_source (0.9.2) method_source (0.9.2)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2022.0105)
mimemagic (0.3.10) mimemagic (0.3.10)
nokogiri (~> 1) nokogiri (~> 1)
rake rake
@ -189,6 +203,7 @@ GEM
mustermann (1.1.1) mustermann (1.1.1)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
mysql2 (0.5.3) mysql2 (0.5.3)
netrc (0.11.0)
nio4r (2.5.2) nio4r (2.5.2)
nokogiri (1.10.8) nokogiri (1.10.8)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
@ -288,6 +303,11 @@ GEM
regexp_parser (1.7.0) regexp_parser (1.7.0)
request_store (1.5.0) request_store (1.5.0)
rack (>= 1.4) rack (>= 1.4)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
reverse_markdown (1.4.0) reverse_markdown (1.4.0)
nokogiri nokogiri
roo (2.8.3) roo (2.8.3)
@ -414,6 +434,9 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.6.1) unicode-display_width (1.6.1)
web-console (3.7.0) web-console (3.7.0)
actionview (>= 5.0) actionview (>= 5.0)
@ -450,14 +473,18 @@ DEPENDENCIES
chromedriver-helper chromedriver-helper
deep_cloneable (~> 3.0.0) deep_cloneable (~> 3.0.0)
diffy diffy
doorkeeper
doorkeeper-jwt
enumerize enumerize
faraday (~> 0.15.4) faraday (~> 0.15.4)
font-awesome-sass (= 4.7.0) font-awesome-sass (= 4.7.0)
gitea-client (~> 0.10.2)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
groupdate (~> 4.1.0) groupdate (~> 4.1.0)
harmonious_dictionary (~> 0.0.1) harmonious_dictionary (~> 0.0.1)
jbuilder (~> 2.5) jbuilder (~> 2.5)
jquery-rails jquery-rails
jwt
kaminari (~> 1.1, >= 1.1.1) kaminari (~> 1.1, >= 1.1.1)
letter_avatar letter_avatar
listen (>= 3.0.5, < 3.2) listen (>= 3.0.5, < 3.2)
@ -489,8 +516,8 @@ DEPENDENCIES
sass-rails (~> 5.0) sass-rails (~> 5.0)
searchkick searchkick
selenium-webdriver selenium-webdriver
sidekiq sidekiq (= 5.2.8)
sidekiq-cron (~> 1.1) sidekiq-cron (= 1.2.0)
simple_form simple_form
simple_xlsx_reader simple_xlsx_reader
sinatra sinatra

View File

@ -121,4 +121,4 @@ You may obtain a copy of Mulan PSL v2 at:
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. See the Mulan PSL v2 for more details.

View File

@ -9,7 +9,7 @@ GitLink确实开源是中国计算机学会CCF官方指定的开源
- **分布式协作开发**基于Git打造分布式代码托管环境提供免费公、私有代码仓库支持在线文件编辑、代码分支管理、协作贡献统计、代码仓库复刻Fork、贡献合并请求PR、群智贡献审阅等功能让您的项目在这里健康、快速的成长 - **分布式协作开发**基于Git打造分布式代码托管环境提供免费公、私有代码仓库支持在线文件编辑、代码分支管理、协作贡献统计、代码仓库复刻Fork、贡献合并请求PR、群智贡献审阅等功能让您的项目在这里健康、快速的成长
- **一站式过程管理**:提供Issue、里程碑、通知提醒、标签归档等多样化任务管理工具支持各类开发任务的发布、指派与跟踪同时提供在线Wiki文档、组织多粒度管理等功能为您搭建一站式的项目过程管理环境让您的团队协作更高效、过程更透明 - **一站式过程管理**:提供Issue、里程碑、通知提醒、标签归档等多样化任务管理工具支持各类开发任务的发布、指派与跟踪同时提供在线Wiki文档、组织多粒度管理等功能为您搭建一站式的项目过程管理环境让您的团队协作更高效、过程更透明
- **高效流水线运维**融合DevOps思想提供轻量级的工作流引擎Engine打通编码、测试、构建、部署等开发运维环节支持自定义配置、代码静态扫描、构建自动触发、容器镜像托管等功能同时支持接入第三方运维工具让您的代码更加快速、可靠地形成高质量的产品 - **高效流水线运维**融合DevOps思想提供轻量级的工作流引擎Engine打通编码、测试、构建、部署等开发运维环节支持自定义配置、代码静态扫描、构建自动触发、容器镜像托管等功能同时支持接入第三方运维工具让您的代码更加快速、可靠地形成高质量的产品
@ -34,18 +34,52 @@ GitLink确实开源是中国计算机学会CCF官方指定的开源
* imagemagick * imagemagick
### 步骤 ### 步骤
1安装 Rails 必要的一些三方库:
1克隆稳定版本 - Mac OS X
```bash
brew install imagemagick ghostscript libxml2 libxslt libiconv
``` ```
- Ubuntu
```bash
sudo apt-get update
sudo apt-get install -y openssl libssl-dev imagemagick git ruby-dev nodejs libmariadb-dev libmysqlclient-dev shared-mime-info libpq-dev libxml2-dev libxslt-dev
sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y tzdata
sudo ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
```
2安装 Ruby, Rails 运行环境:[如何快速正确的安装 Ruby, Rails 运行环境](https://ruby-china.org/wiki/install_ruby_guide)
```bash
#检验环境是否正确
ruby -v
#ruby 2.4.x ...
gem -v
#3.x.x
bundle -v
#Bundler version 2.x.x
rails -v
#Rails 5.2.x
```
3克隆稳定版本
```bash
git clone -b master https://gitlink.org.cn/Gitlink/forgeplus.git git clone -b master https://gitlink.org.cn/Gitlink/forgeplus.git
``` ```
2安装依赖包 4)安装依赖包
```bash ```bash
cd forgeplus && bundle install #进入目录
cd forgeplus
#删除Gemfile.lock
rm -rf Gemfile.lock
#安装依赖包
bundle install
``` ```
3配置初始化文件进入项目根目录执行以下命令 5)配置初始化文件:进入项目根目录执行以下命令
```bash ```bash
cp config/configuration.yml.example config/configuration.yml cp config/configuration.yml.example config/configuration.yml
cp config/database.yml.example config/database.yml cp config/database.yml.example config/database.yml
@ -53,8 +87,8 @@ touch config/redis.yml
touch config/elasticsearch.yml touch config/elasticsearch.yml
``` ```
4)配置数据库:数据库配置信息请查看/config/database.yml文件项目默认采用mysql数据库, 如需更改,请自行修改配置信息,默认配置如下 6)配置数据库:数据库配置信息请查看/config/database.yml文件项目默认采用mysql数据库, 如需更改,请自行修改配置信息,默认配置如下
```bash ```yaml
default: &default default: &default
adapter: mysql2 adapter: mysql2
host: 127.0.0.1 host: 127.0.0.1
@ -63,7 +97,7 @@ default: &default
password: 123456 password: 123456
``` ```
5配置gitea服务(可选)如需要部署自己的gitea平台请参考[gitea官方平台文档](https://docs.gitea.io/zh-cn/install-from-binary/)。因目前gitea平台api受限暂时推荐从forge平台获取[gitea部署文件](https://www.gitlink.org.cn/Gitlink/gitea-binary)进行部署 7配置gitea服务(可选)如需要部署自己的gitea平台请参考[gitea官方平台文档](https://docs.gitea.io/zh-cn/install-from-binary/)。因目前gitea平台api受限暂时推荐从forge平台获取[gitea部署文件](https://www.gitlink.org.cn/Gitlink/gitea-binary)进行部署
- 配置gitea服务步骤 - 配置gitea服务步骤
@ -71,7 +105,7 @@ default: &default
-- 修改forge平台的 config/configuration.yml中的gitea服务指向地址 -- 修改forge平台的 config/configuration.yml中的gitea服务指向地址
```ruby ```yaml
gitea: gitea:
access_key_id: 'root' access_key_id: 'root'
access_key_secret: 'password' access_key_secret: 'password'
@ -79,61 +113,59 @@ gitea:
base_url: '/api/v1' base_url: '/api/v1'
``` ```
6安装redis环境请自行搜索各平台如何安装部署redis环境 8配置/config/database.yml文件(安装redis环境:请自行搜索各平台如何安装部署redis环境)
```yaml
default: &default
url: redis://localhost:6379
db: 1
7安装imagemagick插件 production:
- Mac OS X <<: *default
```bash url: redis://localhost:6379
brew install imagemagick ghostscript
``` ```
- Linux 9创建数据库开发环境为development 生成环境为production
```bash
sudo apt-get install -y imagemagick
```
8创建数据库开发环境为development 生成环境为production
```bash ```bash
rails db:create RAILS_ENV=development rails db:create RAILS_ENV=development
``` ```
9)导入数据表结构 10导入数据表结构
```bash ```bash
bundle exec rake sync_table_structure:import_csv bundle exec rake sync_table_structure:import_csv
``` ```
10执行migrate迁移文件开发环境为development 生成环境为production 11执行migrate迁移文件开发环境为development 生成环境为production
```bash ```bash
rails db:migrate RAILS_ENV=development rails db:migrate RAILS_ENV=development
``` ```
11clone前端代码将前端代码克隆到public/react目录下目录结构应该是: public/react/build 12clone前端代码将前端代码克隆到public/react目录下目录结构应该是: public/react/build
```bash ```bash
git clone -b standalone https://gitlink.org.cn/Gitlink/build.git git clone -b master https://gitlink.org.cn/Gitlink/build.git
``` ```
12启动redis(此处以macOS系统为例) 13启动redis(此处以macOS系统为例)
```bash ```bash
redis-server& redis-server&
``` ```
13启动sidekiq开发环境为development 生成环境为production 14启动sidekiq开发环境为development 生成环境为production
```bash ```bash
bundle exec sidekiq -C config/sidekiq.yml -e production -d bundle exec sidekiq -C config/sidekiq.yml -e production -d
``` ```
14启动rails服务 15启动rails服务
```bash ```bash
rails s rails s
``` ```
15)浏览器访问:在浏览器中输入如下地址访问 16)浏览器访问:在浏览器中输入如下地址访问
```bash ```bash
http://localhost:3000/ http://localhost:3000/
``` ```
16)其他说明:通过页面注册以第一个用户为平台管理员用户 17)其他说明:通过页面注册以第一个用户为平台管理员用户
## 页面展示 ## 页面展示

View File

@ -1,136 +1,137 @@
//= require rails-ujs //= require rails-ujs
//= require activestorage //= require activestorage
//= require turbolinks //= require turbolinks
//= require jquery3 //= require jquery3
//= require popper //= require popper
//= require bootstrap-sprockets //= require bootstrap-sprockets
//= require jquery.validate.min //= require jquery.validate.min
//= require additional-methods.min //= require additional-methods.min
//= require bootstrap-notify //= require bootstrap-notify
//= require jquery.cookie.min //= require jquery.cookie.min
//= require select2 //= require select2
//= require moment.min //= require moment.min
//= require jquery.cxselect //= require jquery.cxselect
//= require bootstrap-datepicker //= require bootstrap-datepicker
//= require bootstrap-datetimepicker //= require bootstrap-datetimepicker
//= require bootstrap.viewer //= require bootstrap.viewer
//= require jquery.mloading //= require bootstrap/bootstrap-toggle
//= require jquery-confirm.min //= require jquery.mloading
//= require common //= require jquery-confirm.min
//= require common
//= require echarts
//= require codemirror/lib/codemirror //= require echarts
//= require codemirror/mode/shell/shell //= require codemirror/lib/codemirror
//= require editormd/editormd //= require codemirror/mode/shell/shell
//= require editormd/languages/zh-tw //= require editormd/editormd
//= require dragula/dragula //= require editormd/languages/zh-tw
//= require dragula/dragula
//= require_tree ./i18n
//= require_tree ./admins //= require_tree ./i18n
//= require_tree ./admins
$.ajaxSetup({
beforeSend: function(xhr) { $.ajaxSetup({
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')); beforeSend: function(xhr) {
} xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
}); }
});
// ******** select2 global config ********
$.fn.select2.defaults.set('theme', 'bootstrap4'); // ******** select2 global config ********
$.fn.select2.defaults.set('language', 'zh-CN'); $.fn.select2.defaults.set('theme', 'bootstrap4');
$.fn.select2.defaults.set('language', 'zh-CN');
Turbolinks.setProgressBarDelay(200);
Turbolinks.setProgressBarDelay(200);
$.notifyDefaults({
type: 'success', $.notifyDefaults({
z_index: 9999, type: 'success',
delay: 2000 z_index: 9999,
}); delay: 2000
});
function show_success_flash(){
$.notify({ function show_success_flash(){
message: '操作成功' $.notify({
},{ message: '操作成功'
type: 'success' },{
}); type: 'success'
} });
}
$(document).on('turbolinks:load', function(){
$('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' }); $(document).on('turbolinks:load', function(){
$('[data-toggle="popover"]').popover(); $('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' });
$('[data-toggle="popover"]').popover();
// 图片查看大图
$('img.preview-image').bootstrapViewer(); // 图片查看大图
$('img.preview-image').bootstrapViewer();
// flash alert提示框自动关闭
if($('.admin-alert-container .alert').length > 0){ // flash alert提示框自动关闭
setTimeout(function(){ if($('.admin-alert-container .alert').length > 0){
$('.admin-alert-container .alert:not(.alert-danger)').alert('close'); setTimeout(function(){
}, 2000); $('.admin-alert-container .alert:not(.alert-danger)').alert('close');
setTimeout(function(){ }, 2000);
$('.admin-alert-container .alert.alert-danger').alert('close'); setTimeout(function(){
}, 5000); $('.admin-alert-container .alert.alert-danger').alert('close');
} }, 5000);
}); }
});
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide'); $(document).on("turbolinks:before-cache", function () {
$('[data-toggle="popover"]').popover('hide'); $('[data-toggle="tooltip"]').tooltip('hide');
}); $('[data-toggle="popover"]').popover('hide');
// var progressBar = new Turbolinks.ProgressBar(); });
// var progressBar = new Turbolinks.ProgressBar();
// $(document).on('ajax:send', function(event){
// console.log('ajax send', event); // $(document).on('ajax:send', function(event){
// progressBar.setValue(0) // console.log('ajax send', event);
// progressBar.show() // progressBar.setValue(0)
// }); // progressBar.show()
// // });
// $(document).on('ajax:complete', function(event){ //
// console.log('ajax complete', event); // $(document).on('ajax:complete', function(event){
// progressBar.setValue(1) // console.log('ajax complete', event);
// progressBar.hide() // 分页时不触发,奇怪 // progressBar.setValue(1)
// }); // progressBar.hide() // 分页时不触发,奇怪
// $(document).on('ajax:success', function(event){ // });
// console.log('ajax success', event); // $(document).on('ajax:success', function(event){
// }); // console.log('ajax success', event);
// $(document).on('ajax:error', function(event){ // });
// console.log('ajax error', event); // $(document).on('ajax:error', function(event){
// }); // console.log('ajax error', event);
// });
$(function () {
}); $(function () {
});
$(document).on('turbolinks:load', function() {
$(document).on('turbolinks:load', function() {
$('.logo-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this); $('.logo-item-left').on("change", 'input[type="file"]', function () {
var file = this.files[0]; var $fileInput = $(this);
var imageType = /image.*/; var file = this.files[0];
if (file && file.type.match(imageType)) { var imageType = /image.*/;
var reader = new FileReader(); if (file && file.type.match(imageType)) {
reader.onload = function () { var reader = new FileReader();
var $box = $fileInput.parent(); reader.onload = function () {
$box.find('img').attr('src', reader.result).css('display', 'block'); var $box = $fileInput.parent();
$box.addClass('has-img'); $box.find('img').attr('src', reader.result).css('display', 'block');
}; $box.addClass('has-img');
reader.readAsDataURL(file); };
} else { reader.readAsDataURL(file);
} } else {
}); }
});
$('.attachment-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this); $('.attachment-item-left').on("change", 'input[type="file"]', function () {
var file = this.files[0]; var $fileInput = $(this);
var imageType = /image.*/; var file = this.files[0];
if (file && file.type.match(imageType)) { var imageType = /image.*/;
var reader = new FileReader(); if (file && file.type.match(imageType)) {
reader.onload = function () { var reader = new FileReader();
var $box = $fileInput.parent(); reader.onload = function () {
$box.find('img').attr('src', reader.result).css('display', 'block'); var $box = $fileInput.parent();
$box.addClass('has-img'); $box.find('img').attr('src', reader.result).css('display', 'block');
}; $box.addClass('has-img');
reader.readAsDataURL(file); };
} else { reader.readAsDataURL(file);
} } else {
}); }
});
}) })

View File

@ -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);

View File

@ -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

View File

@ -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"}

View File

@ -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);

View File

@ -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

View File

@ -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"}

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -8,6 +8,7 @@
@import "jquery.mloading"; @import "jquery.mloading";
@import "jquery-confirm.min"; @import "jquery-confirm.min";
@import "bootstrap-datetimepicker.min"; @import "bootstrap-datetimepicker.min";
@import "bootstrap/bootstrap-toggle.min";
@import "codemirror/lib/codemirror"; @import "codemirror/lib/codemirror";
@import "editormd/css/editormd.min"; @import "editormd/css/editormd.min";
@ -203,4 +204,14 @@ input.form-control {
color: #23272B; color: #23272B;
font-size: 1rem; 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;
} }

View File

@ -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; }

View File

@ -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}

View File

@ -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; }

View File

@ -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}

View File

@ -0,0 +1,3 @@
// Place all the styles related to the log controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -0,0 +1,84 @@
body {
background-color: #fff;
color: #333;
margin: 33px;
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
}
p, ol, ul, td {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 13px;
line-height: 18px;
}
pre {
background-color: #eee;
padding: 10px;
font-size: 11px;
}
a {
color: #000;
&:visited {
color: #666;
}
&:hover {
color: #fff;
background-color: #000;
}
}
th {
padding-bottom: 5px;
}
td {
padding: 0 5px 7px;
}
div {
&.field, &.actions {
margin-bottom: 10px;
}
}
#notice {
color: green;
}
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
#error_explanation {
width: 450px;
border: 2px solid red;
padding: 7px 7px 0;
margin-bottom: 20px;
background-color: #f0f0f0;
h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px -7px 0;
background-color: #c00;
color: #fff;
}
ul li {
font-size: 12px;
list-style: square;
}
}
label {
display: block;
}

View File

@ -0,0 +1,3 @@
// Place all the styles related to the SponsorTiers controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -0,0 +1,3 @@
// Place all the styles related to the Sponsorships controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -0,0 +1,3 @@
// Place all the styles related to the Wallets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,6 +1,25 @@
class AccountsController < ApplicationController class AccountsController < ApplicationController
before_action :require_login, only: [:login_check, :simple_update]
include ApplicationHelper include ApplicationHelper
#skip_before_action :check_account, :only => [:logout]
def simple_update
simple_update_params.merge!(username: params[:username]&.gsub(/\s+/, ""))
simple_update_params.merge!(email: params[:email]&.gsub(/\s+/, ""))
simple_update_params.merge!(platform: (params[:platform] || 'forge')&.gsub(/\s+/, ""))
Register::RemoteForm.new(simple_update_params).validate!
ActiveRecord::Base.transaction do
result = auto_update(current_user, simple_update_params)
if result[:message].blank?
render_ok
else
render_error(result[:message])
end
end
end
def index def index
render json: session render json: session
end end
@ -92,7 +111,9 @@ class AccountsController < ApplicationController
sync_params = { sync_params = {
password: params[:password].to_s, 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) interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
@ -120,6 +141,7 @@ class AccountsController < ApplicationController
Register::Form.new(register_params).validate! Register::Form.new(register_params).validate!
user = Users::RegisterService.call(register_params) user = Users::RegisterService.call(register_params)
user.mail = "#{user.login}@example.org" if user.mail.blank?
password = register_params[:password].strip password = register_params[:password].strip
# gitea用户注册, email, username, password # gitea用户注册, email, username, password
@ -131,6 +153,10 @@ class AccountsController < ApplicationController
user.gitea_uid = gitea_user[:body]['id'] user.gitea_uid = gitea_user[:body]['id']
if user.save! if user.save!
UserExtension.create!(user_id: user.id) UserExtension.create!(user_id: user.id)
# 绑定授权账号
if ["qq", "wechat", "gitee", "github", "educoder"].include?(params[:type].to_s) && session[:unionid].present?
"OpenUsers::#{params[:type].to_s.capitalize}".constantize.create!(user: user, uid: session[:unionid])
end
successful_authentication(user) successful_authentication(user)
render_ok render_ok
end end
@ -244,6 +270,7 @@ class AccountsController < ApplicationController
set_autologin_cookie(user) set_autologin_cookie(user)
UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip) UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip)
# user.daily_reward
user.update_column(:last_login_on, Time.now) user.update_column(:last_login_on, Time.now)
session[:"#{default_yun_session}"] = user.id session[:"#{default_yun_session}"] = user.id
Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}") Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}")
@ -315,7 +342,12 @@ class AccountsController < ApplicationController
Register::CheckColumnsForm.new(check_params).validate! Register::CheckColumnsForm.new(check_params).validate!
render_ok render_ok
end end
def login_check
Register::LoginCheckColumnsForm.new(check_params.merge(user: current_user)).validate!
render_ok
end
private private
# type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 # type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
@ -358,7 +390,7 @@ class AccountsController < ApplicationController
params.require(:user).permit(:login, :email, :phone) params.require(:user).permit(:login, :email, :phone)
end end
def login_params def login_params
params.require(:account).permit(:login, :password) params.require(:account).permit(:login, :password)
end end
@ -367,13 +399,13 @@ class AccountsController < ApplicationController
end end
def register_params def register_params
params.permit(:login, :namespace, :password, :password_confirmation, :code) params.permit(:login, :namespace, :password, :password_confirmation, :code, :type)
end end
def reset_password_params def reset_password_params
params.permit(:login, :password, :password_confirmation, :code) params.permit(:login, :password, :password_confirmation, :code)
end end
def find_user def find_user
phone_or_mail = strip(reset_password_params[:login]) phone_or_mail = strip(reset_password_params[:login])
User.where("phone = :search OR mail = :search", search: phone_or_mail).last User.where("phone = :search OR mail = :search", search: phone_or_mail).last
@ -382,5 +414,8 @@ class AccountsController < ApplicationController
def remote_register_params def remote_register_params
params.permit(:username, :email, :password, :platform) params.permit(:username, :email, :password, :platform)
end end
def simple_update_params
params.permit(:username, :email, :password, :platform)
end
end end

View File

@ -1,10 +1,33 @@
class Admins::DashboardsController < Admins::BaseController class Admins::DashboardsController < Admins::BaseController
def index def index
@active_user_count = User.where(last_login_on: today).count # 用户活跃数
@weekly_active_user_count = User.where(last_login_on: current_week).count day_user_ids = CommitLog.where(created_at: today).pluck(:user_id).uniq
@month_active_user_count = User.where(last_login_on: current_month).count 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 end
def month_active_user def month_active_user
@ -16,7 +39,6 @@ class Admins::DashboardsController < Admins::BaseController
{ value: count['professional'].to_i, name: '专业人士' }, { value: count['professional'].to_i, name: '专业人士' },
{ value: count[nil].to_i, name: '未选职业' }, { value: count[nil].to_i, name: '未选职业' },
] ]
render_ok(data: data) render_ok(data: data)
end end
@ -42,10 +64,14 @@ class Admins::DashboardsController < Admins::BaseController
end end
def current_week 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 end
def current_month 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
end end

View File

@ -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

View File

@ -2,7 +2,7 @@ class Admins::ImportUsersController < Admins::BaseController
def create def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile) return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportUserService.call(params[:file].to_io) result = Admins::ImportUserFromExcelService.call(params[:file].to_io)
render_ok(result) render_ok(result)
rescue Admins::ImportUserService::Error => ex rescue Admins::ImportUserService::Error => ex
render_error(ex) render_error(ex)

View File

@ -2,8 +2,24 @@ class Admins::MessageTemplatesController < Admins::BaseController
before_action :get_template, only: [:edit, :update, :destroy] before_action :get_template, only: [:edit, :update, :destroy]
def index def index
message_templates = MessageTemplate.group(:type).count.keys message_templates = MessageTemplate.ransack(sys_notice_or_email_or_email_title_cont: params[:search]).result
@message_templates = kaminari_array_paginate(message_templates) @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 end
def edit def edit
@ -31,7 +47,9 @@ class Admins::MessageTemplatesController < Admins::BaseController
private private
def message_template_params 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 end
def get_template def get_template

View File

@ -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

View File

@ -1,6 +1,6 @@
class Admins::ProjectIgnoresController < Admins::BaseController class Admins::ProjectIgnoresController < Admins::BaseController
before_action :set_ignore, only: [:edit,:update, :destroy,:show] 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 def index
sort_by = Ignore.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' 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) @project_ignore = Ignore.new(ignore_params)
if @project_ignore.save! if @project_ignore.save
redirect_to admins_project_ignores_path redirect_to admins_project_ignores_path
flash[:success] = "创建成功" flash[:success] = "创建成功"
else else
render :new redirect_to admins_project_ignores_path
flash[:danger] = "创建失败" flash[:danger] = @project_ignore.errors.full_messages.join(",")
end end
end end
@ -58,8 +58,8 @@ class Admins::ProjectIgnoresController < Admins::BaseController
redirect_to admins_project_ignores_path redirect_to admins_project_ignores_path
flash[:success] = "更新成功" flash[:success] = "更新成功"
else else
render :edit redirect_to admins_project_ignores_path
flash[:danger] = "更新失败" flash[:danger] = @project_ignore.errors.full_messages.join(",")
end end
end end
@ -98,23 +98,23 @@ class Admins::ProjectIgnoresController < Admins::BaseController
params.require(:ignore).permit(:name,:content) params.require(:ignore).permit(:name,:content)
end end
def validate_params # def validate_params
name = params[:ignore][:name] # name = params[:ignore][:name]
if name.blank? # if name.blank?
flash[:danger] = "名称不允许为空" # flash[:danger] = "名称不允许为空"
redirect_to admins_project_ignores_path # redirect_to admins_project_ignores_path
elsif check_ignore_present?(name) && @project_ignore.blank? # elsif check_ignore_present?(name) && @project_ignore.blank?
flash[:danger] = "创建失败:名称已存在" # flash[:danger] = "创建失败:名称已存在"
redirect_to admins_project_ignores_path # redirect_to admins_project_ignores_path
end # end
end # end
def check_ignore_present?(name) # def check_ignore_present?(name)
return true if name.blank? # return true if name.blank?
name_downcase = name.downcase # name_downcase = name.downcase
name_upcase = name.upcase # name_upcase = name.upcase
name_first_big = name.capitalize # name_first_big = name.capitalize
Ignore.exists?(name: name_downcase) || Ignore.exists?(name: name_upcase) || Ignore.exists?(name: name_first_big) # Ignore.exists?(name: name_downcase) || Ignore.exists?(name: name_upcase) || Ignore.exists?(name: name_first_big)
end # end
end end

View File

@ -27,17 +27,18 @@ class Admins::ProjectLanguagesController < Admins::BaseController
flash[:success] = '创建成功' flash[:success] = '创建成功'
else else
redirect_to admins_project_languages_path redirect_to admins_project_languages_path
flash[:danger] = '创建失败' flash[:danger] = @project_language.errors.full_messages.join(",")
end end
end end
def update def update
if @project_language.update_attribute(:name, @name) @project_language.attributes = {name: @name}
if @project_language.save
redirect_to admins_project_languages_path redirect_to admins_project_languages_path
flash[:success] = '更新成功' flash[:success] = '更新成功'
else else
redirect_to admins_project_languages_path redirect_to admins_project_languages_path
flash[:success] = '更新失败' flash[:danger] = @project_language.errors.full_messages.join(",")
end end
end end

View File

@ -1,6 +1,6 @@
class Admins::ProjectLicensesController < Admins::BaseController class Admins::ProjectLicensesController < Admins::BaseController
before_action :set_license, only: [:edit,:update, :destroy,:show] 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 def index
sort_by = License.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' 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 # position: max_position
# } # }
@project_license = License.new(license_params) @project_license = License.new(license_params)
if @project_license.save
if @project_license.save!
redirect_to admins_project_licenses_path redirect_to admins_project_licenses_path
flash[:success] = "创建成功" flash[:success] = "创建成功"
else else
render :new redirect_to admins_project_licenses_path
flash[:danger] = "创建失败" flash[:danger] = @project_license.errors.full_messages.join(",")
end end
end end
@ -54,12 +53,13 @@ class Admins::ProjectLicensesController < Admins::BaseController
# permissions: permissions.to_s, # permissions: permissions.to_s,
# limitations: limitations.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 redirect_to admins_project_licenses_path
flash[:success] = "更新成功" flash[:success] = "更新成功"
else else
render :edit render admins_project_licenses_path
flash[:danger] = "更新失败" flash[:danger] = @project_license.errors.full_messages.join(",")
end end
end end
@ -98,23 +98,23 @@ class Admins::ProjectLicensesController < Admins::BaseController
params.require(:license).permit(:name,:content) params.require(:license).permit(:name,:content)
end end
def validate_params # def validate_params
name = params[:license][:name] # name = params[:license][:name]
if name.blank? # if name.blank?
flash[:danger] = "名称不允许为空" # flash[:danger] = "名称不允许为空"
redirect_to admins_project_licenses_path # redirect_to admins_project_licenses_path
elsif check_license_present?(name) && @project_license.blank? # elsif check_license_present?(name) && @project_license.blank?
flash[:danger] = "创建失败:名称已存在" # flash[:danger] = "创建失败:名称已存在"
redirect_to admins_project_licenses_path # redirect_to admins_project_licenses_path
end # end
end # end
def check_license_present?(name) # def check_license_present?(name)
return true if name.blank? # return true if name.blank?
name_downcase = name.downcase # name_downcase = name.downcase
name_upcase = name.upcase # name_upcase = name.upcase
name_first_big = name.capitalize # name_first_big = name.capitalize
License.exists?(name: name_downcase) || License.exists?(name: name_upcase) || License.exists?(name: name_first_big) # License.exists?(name: name_downcase) || License.exists?(name: name_upcase) || License.exists?(name: name_first_big)
end # end
end end

View File

@ -1,8 +1,9 @@
class Admins::Topic::BannersController < Admins::Topic::BaseController class Admins::Topic::BannersController < Admins::Topic::BaseController
before_action :find_banner, only: [:edit, :update, :destroy] before_action :find_banner, only: [:edit, :update, :destroy]
def index def index
@banners = paginate(::Topic::Banner) @banners = paginate(::Topic::Banner)
@banners = paginate(::Topic::Banner.where("title like ?", "%#{params[:search]}%")) if params[:search].present?
end end
def new def new
@ -52,6 +53,6 @@ class Admins::Topic::BannersController < Admins::Topic::BaseController
end end
def banner_params def banner_params
params.require(:topic_banner).permit(:title, :order_index) params.require(:topic_banner).permit(:title, :order_index, :url)
end end
end end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,105 @@
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 check_phone_verify_code
code = strip(params[:code])
phone = strip(params[:phone])
code_type = params[:code_type]
return tip_exception(-2, "手机号格式有误") unless phone =~ CustomRegexp::PHONE
verifi_code = VerificationCode.where(phone: phone, 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
def update_phone
@result_object = Api::V1::Users::UpdatePhoneService.call(@observe_user, params)
if @result_object
return render_ok
else
return render_error('更改手机号失败!')
end
end
end

View File

@ -10,21 +10,25 @@ class ApplicationController < ActionController::Base
include LoggerHelper include LoggerHelper
include LoginHelper include LoginHelper
include RegisterHelper include RegisterHelper
include UpdateHelper
protect_from_forgery prepend: true, unless: -> { request.format.json? } protect_from_forgery prepend: true, unless: -> { request.format.json? }
before_action :check_sign before_action :check_sign
before_action :user_setup before_action :user_setup
#before_action :check_account #before_action :check_account
after_action :user_trace_log
# TODO # TODO
# check sql query time # check sql query time
before_action do before_action do
if request.subdomain === 'testforgeplus' || request.subdomain === "profiler" # if request.subdomain === 'testforgeplus' || request.subdomain === "profiler"
Rack::MiniProfiler.authorize_request # Rack::MiniProfiler.authorize_request
end # end
end end
before_action :update_last_login_on
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z) DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
OPENKEY = Rails.application.config_for(:configuration)['sign_key'] || "79e33abd4b6588941ab7622aed1e67e8" OPENKEY = Rails.application.config_for(:configuration)['sign_key'] || "79e33abd4b6588941ab7622aed1e67e8"
@ -79,11 +83,10 @@ class ApplicationController < ActionController::Base
# 判断用户的邮箱或者手机是否可用 # 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册2忘记密码3绑定 # params[:type] 1: 注册2忘记密码3绑定
def check_mail_and_phone_valid login, type def check_mail_and_phone_valid login, type
unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/ || unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱") tip_exception(-2, "请输入正确的手机号或邮箱")
end end
user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login) user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login)
if user_exist && type.to_i == 1 if user_exist && type.to_i == 1
tip_exception(-2, "该手机号码或邮箱已被注册") tip_exception(-2, "该手机号码或邮箱已被注册")
@ -103,8 +106,10 @@ class ApplicationController < ActionController::Base
when 1, 2, 4, 9 when 1, 2, 4, 9
# 手机类型的发送 # 手机类型的发送
sigle_para = {phone: value} sigle_para = {phone: value}
status = Gitlink::Sms.send(mobile: value, code: code) # status = Gitlink::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0 # tip_exception(-2, code_msg(status)) if status != 0
status = Sms::UcloudService.call(value, code, send_type)
tip_exception(-2, ucloud_code_msg(status)) if status != 0
when 8, 3, 5 when 8, 3, 5
# 邮箱类型的发送 # 邮箱类型的发送
sigle_para = {email: value} sigle_para = {email: value}
@ -116,8 +121,13 @@ class ApplicationController < ActionController::Base
send_email_control = LimitForbidControl::SendEmailCode.new(value) send_email_control = LimitForbidControl::SendEmailCode.new(value)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid? tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin begin
UserMailer.register_email(value, code).deliver_now if send_type == 3
UserMailer.find_password(value, code).deliver_now
elsif send_type == 5
UserMailer.bind_email(value, code).deliver_now
else
UserMailer.register_email(value, code).deliver_now
end
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute) Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment! send_email_control.increment!
# Mailer.run.email_register(code, value) # Mailer.run.email_register(code, value)
@ -149,6 +159,27 @@ class ApplicationController < ActionController::Base
end end
end end
def ucloud_code_msg status
case status
when 0
"验证码已经发送到您的手机,请注意查收"
when 171
"API签名错误"
when 18014
"无效手机号码"
when 18017
"无效模板"
when 18018
"短信模板参数与短信模板不匹配"
when 18023
"短信内容中含有运营商拦截的关键词"
when 18033
"变量内容不符合规范"
else
"错误码#{status}"
end
end
def validate_type(object_type) def validate_type(object_type)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip) normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end end
@ -170,7 +201,25 @@ class ApplicationController < ActionController::Base
# 未授权的捕捉407弹试用申请弹框 # 未授权的捕捉407弹试用申请弹框
def require_login def require_login
#6.13 -hs #6.13 -hs
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
def require_login_or_token
if params[:token].present?
user = User.try_to_autologin(params[:token])
User.current = user
end
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
def require_login_cloud_ide_saas
if params[:sign].present? && params[:email].present?
sign = Digest::MD5.hexdigest("#{OPENKEY}#{params[:email]}")
if params[:sign].to_s == sign
user = User.find_by(mail: params[:email])
User.current = user
end
end
tip_exception(401, "请登录后再操作") unless User.current.logged? tip_exception(401, "请登录后再操作") unless User.current.logged?
end end
@ -249,42 +298,58 @@ class ApplicationController < ActionController::Base
#return if params[:controller] == "main" #return if params[:controller] == "main"
# Find the current user # Find the current user
#Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}") #Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}")
User.current = find_current_user if request.headers["Authorization"].present? && request.headers["Authorization"].start_with?('Bearer')
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous")) tip_exception(401, "请登录后再操作!") unless valid_doorkeeper_token?
if @doorkeeper_token.present?
# 开放课程通过链接访问的用户 # client方法对接需要一直带着用户标识uid
if !User.current.logged? && !params[:chinaoocTimestamp].blank? && !params[:websiteName].blank? && !params[:chinaoocKey].blank? if @doorkeeper_token.resource_owner_id.blank?
content = "#{OPENKEY}#{params[:websiteName]}#{params[:chinaoocTimestamp]}" tip_exception(-1, "缺少用户标识!") if params[:uid].nil?
User.current = User.find(params[:uid])
if Digest::MD5.hexdigest(content) == params[:chinaoocKey] else
user = open_class_user User.current = User.find_by(id: @doorkeeper_token.resource_owner_id)
if user
start_user_session(user)
set_autologin_cookie(user)
end end
User.current = user
end end
end else
# if !User.current.logged? && Rails.env.development? User.current = find_current_user
# User.current = User.find 1 uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
# end
# 开放课程通过链接访问的用户
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}") logger.info("subdomain:#{request.subdomain}")
if request.subdomain != "www" if request.subdomain != "www"
if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
User.current = User.find 81403 User.current = User.find 81403
elsif params[:debug] == 'student' elsif params[:debug] == 'student'
User.current = User.find 8686 User.current = User.find 8686
elsif params[:debug] == 'admin' elsif params[:debug] == 'admin'
logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....." logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
user = User.find 35 user = User.find 36480
User.current = user User.current = user
cookies.signed[:user_id] = user.id cookies.signed[:user_id] = user.id
end
end end
end end
#User.current = User.find 35 # User.current = User.find 81403
end end
# Returns the current user or nil if no user is logged in # Returns the current user or nil if no user is logged in
@ -302,7 +367,19 @@ class ApplicationController < ActionController::Base
# RSS key authentication does not start a session # RSS key authentication does not start a session
User.find_by_rss_key(params[:key]) User.find_by_rss_key(params[:key])
end end
end end
def user_trace_log
user = current_user
# print("*********************url:", request.url, "****routes", request.request_method)
Rails.logger.user_trace.info("{id: #{user.id}, login: #{user.login}, url: #{request.url}, method: #{request.method}, params: #{params}, response_code: #{response.code}, time: #{Time.now}}")
end
def user_trace_update_log(old_value_hash)
user = current_user
str = "{id: #{user.id}, login: #{user.login}, url: #{request.url}, method: #{request.method}, params: #{params.merge(old_value: old_value_hash)}, response_code: #{response.code}, time: #{Time.now}}"
Rails.logger.user_trace.info(str)
end
def try_to_autologin def try_to_autologin
if cookies[autologin_cookie_name] if cookies[autologin_cookie_name]
@ -328,12 +405,17 @@ class ApplicationController < ActionController::Base
respond_to do |format| respond_to do |format|
format.json format.json
end end
end end
## 输出错误信息 ## 输出错误信息
def error_status(message = nil) def error_status(message = nil)
@status = -1 @status = -1
@message = message @message = message
end
# 实训等对应的仓库地址
def repo_ip_url(repo_path)
"#{edu_setting('git_address_ip')}/#{repo_path}"
end end
def repo_url(repo_path) def repo_url(repo_path)
@ -572,6 +654,23 @@ class ApplicationController < ActionController::Base
ss ss
end end
def strip_html(text, len=0, endss="...")
ss = ""
if !text.nil? && text.length>0
ss=text.gsub(/<\/?.*?>/, '').strip
ss = ss.gsub(/&nbsp;*/, '')
ss = ss.gsub(/\r\n/,'') #新增
ss = ss.gsub(/\n/,'') #新增
if len > 0 && ss.length > len
ss = ss[0, len] + endss
elsif len > 0 && ss.length <= len
ss = ss
#ss = truncate(ss, :length => len)
end
end
ss
end
# Returns a string that can be used as filename value in Content-Disposition header # Returns a string that can be used as filename value in Content-Disposition header
def filename_for_content_disposition(name) def filename_for_content_disposition(name)
request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name request.env['HTTP_USER_AGENT'] =~ %r{MSIE|Trident|Edge} ? ERB::Util.url_encode(name) : name
@ -583,8 +682,8 @@ class ApplicationController < ActionController::Base
# 获取Oauth Client # 获取Oauth Client
def get_client(site) def get_client(site)
client_id = Rails.configuration.Gitlink['client_id'] client_id = Rails.configuration.educoder['client_id']
client_secret = Rails.configuration.Gitlink['client_secret'] client_secret = Rails.configuration.educoder['client_secret']
OAuth2::Client.new(client_id, client_secret, site: site) OAuth2::Client.new(client_id, client_secret, site: site)
end end
@ -604,7 +703,7 @@ class ApplicationController < ActionController::Base
def kaminari_paginate(relation) def kaminari_paginate(relation)
limit = params[:limit] || params[:per_page] limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i page = params[:page].to_i.zero? ? 1 : params[:page].to_i
relation.page(page).per(limit) relation.page(page).per(limit)
@ -681,7 +780,7 @@ class ApplicationController < ActionController::Base
@project, @owner = Project.find_with_namespace(namespace, id) @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" logger.info "########### has project and can read project"
@project @project
# elsif @project && current_user.is_a?(AnonymousUser) # elsif @project && current_user.is_a?(AnonymousUser)
@ -689,9 +788,15 @@ class ApplicationController < ActionController::Base
# @project = nil if !@project.is_public? # @project = nil if !@project.is_public?
# render_forbidden and return # render_forbidden and return
else else
logger.info "###########project not found" if @project.present?
@project = nil logger.info "########### has project and but can't read project"
render_not_found and return @project = nil
render_forbidden and return
else
logger.info "###########project not found"
@project = nil
render_not_found and return
end
end end
@project @project
end end
@ -732,21 +837,54 @@ class ApplicationController < ActionController::Base
end end
private private
def object_not_found def update_last_login_on
uid_logger("Missing template or cant't find record, responding with 404") if current_user.logged?
render json: {message: "您访问的页面不存在或已被删除", status: 404} current_user.update_column(:last_login_on, Time.now)
false end
end end
def object_not_found
uid_logger("Missing template or cant't find record, responding with 404")
render json: {message: "您访问的页面不存在或已被删除", status: 404}
false
end
def tip_show(exception) def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}") uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json render json: exception.tip_json
end end
def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end
def set_export_cookies def set_export_cookies
cookies[:fileDownload] = true cookies[:fileDownload] = true
end end
# 149课程的评审用户数据创建包含创建课堂学生
def open_class_user
user = User.find_by(login: "OpenClassUser")
unless user
ActiveRecord::Base.transaction do
user_params = {status: 1, login: "OpenClassUser", lastname: "开放课程",
nickname: "开放课程", professional_certification: 1, certification: 1, grade: 0,
password: "12345678", phone: "11122223333", profile_completed: 1}
user = User.create!(user_params)
UserExtension.create!(user_id: user.id, gender: 0, school_id: 3396, :identity => 1, :student_id => "openclassuser") # 3396
subject = Subject.find_by(id: 149)
if subject
subject.courses.each do |course|
CourseMember.create!(course_id: course.id, role: 3, user_id: user.id) if !course.course_members.exists?(user_id: user.id)
end
end
end
end
user
end
# 记录热门搜索关键字 # 记录热门搜索关键字
def record_search_keyword def record_search_keyword
keyword = params[:keyword].to_s.strip keyword = params[:keyword].to_s.strip

View File

@ -1,261 +1,258 @@
#coding=utf-8 #coding=utf-8
# #
# 文件上传 # 文件上传
class AttachmentsController < ApplicationController class AttachmentsController < ApplicationController
before_action :require_login, :check_auth, except: [:show, :preview_attachment, :get_file] before_action :require_login, :check_auth, except: [:show, :preview_attachment, :get_file]
before_action :find_file, only: %i[show destroy] before_action :find_file, only: %i[show destroy]
before_action :attachment_candown, only: [:show] before_action :attachment_candown, only: [:show]
skip_before_action :check_sign, only: [:show, :create] skip_before_action :check_sign, only: [:show, :create]
include ApplicationHelper include ApplicationHelper
def show def show
# 1. 优先跳到cdn # 1. 优先跳到cdn
# 2. 如果没有cdnsend_file # 2. 如果没有cdnsend_file
if @file.cloud_url.present? if @file.cloud_url.present?
update_downloads(@file) update_downloads(@file)
redirect_to @file.cloud_url and return redirect_to @file.cloud_url and return
end end
type_attachment = params[:disposition] || "attachment" type_attachment = params[:disposition] || "attachment"
if type_attachment == "inline" if type_attachment == "inline"
send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf' send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf'
elsif type_attachment == "MP4" elsif type_attachment == "MP4"
send_file_with_range absolute_path(local_path(@file)), disposition: 'inline', type: "video/mp4", range: true send_file_with_range absolute_path(local_path(@file)), disposition: 'inline', type: "video/mp4", range: true
else else
send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream') send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream')
end end
update_downloads(@file) update_downloads(@file)
end end
def get_file def get_file
normal_status(-1, "参数缺失") if params[:download_url].blank? normal_status(-1, "参数缺失") if params[:download_url].blank?
url = URI.encode(params[:download_url].to_s.gsub("http:", "https:")) 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) if url.starts_with?(base_url) && !url.starts_with?("#{base_url}/repo")
domain = Gitea.gitea_config[:domain] domain = GiteaService.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url] api_url = GiteaService.gitea_config[:base_url]
url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?') 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 request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
response = Faraday.get(request_url) response = Faraday.get(request_url)
filename = url.to_s.split("/").pop() filename = url.to_s.split("/").pop()
else else
response = Faraday.get(url) response = Faraday.get(url)
filename = params[:download_url].to_s.split("/").pop() filename = params[:download_url].to_s.split("/").pop()
end end
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment') send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
end end
def create def create
# 1. 本地存储 # 1. 本地存储
# 2. 上传到云 # 2. 上传到云
begin begin
upload_file = params["file"] || params["#{params[:file_param_name]}"]# 这里的file_param_name是为了方便其他插件名称 upload_file = params["file"] || params["#{params[:file_param_name]}"]# 这里的file_param_name是为了方便其他插件名称
uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}") uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}")
raise "未上传文件" unless upload_file raise "未上传文件" unless upload_file
folder = file_storage_directory folder = file_storage_directory
raise "存储目录未定义" unless folder.present? raise "存储目录未定义" unless folder.present?
month_folder = current_month_folder month_folder = current_month_folder
save_path = File.join(folder, month_folder) save_path = File.join(folder, month_folder)
ext = file_ext(upload_file.original_filename) ext = file_ext(upload_file.original_filename)
local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext) local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext)
content_type = upload_file.content_type.presence || 'application/octet-stream' 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 = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type)
remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端 remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端
logger.info "local_path: #{local_path}" logger.info "local_path: #{local_path}"
logger.info "remote_path: #{remote_path}" logger.info "remote_path: #{remote_path}"
disk_filename = local_path[save_path.size + 1, local_path.size] disk_filename = local_path[save_path.size + 1, local_path.size]
#存数据库 #存数据库
# #
@attachment = Attachment.where(disk_filename: disk_filename, @attachment = Attachment.where(disk_filename: disk_filename,
author_id: current_user.id, author_id: current_user.id,
cloud_url: remote_path).first cloud_url: remote_path).first
if @attachment.blank? if @attachment.blank?
@attachment = Attachment.new @attachment = Attachment.new
@attachment.filename = upload_file.original_filename @attachment.filename = upload_file.original_filename
@attachment.disk_filename = local_path[save_path.size + 1, local_path.size] @attachment.disk_filename = local_path[save_path.size + 1, local_path.size]
@attachment.filesize = upload_file.tempfile.size @attachment.filesize = upload_file.tempfile.size
@attachment.content_type = content_type @attachment.content_type = content_type
@attachment.digest = digest @attachment.digest = digest
@attachment.author_id = current_user.id @attachment.author_id = current_user.id
@attachment.disk_directory = month_folder @attachment.disk_directory = month_folder
@attachment.cloud_url = remote_path @attachment.cloud_url = remote_path
@attachment.save! @attachment.save!
else else
logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}" logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}"
end end
render_json render_json
rescue => e rescue => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)
end end
end end
def destroy def destroy
begin begin
@file_path = absolute_path(local_path(@file)) @file_path = absolute_path(local_path(@file))
#return normal_status(403, "") unless @file.author == current_user #return normal_status(403, "") unless @file.author == current_user
@file.destroy! @file.destroy!
delete_file(@file_path) delete_file(@file_path)
normal_status("删除成功") normal_status("删除成功")
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
end end
# 附件为视频时,点击播放 # 附件为视频时,点击播放
def preview_attachment def preview_attachment
attachment = Attachment.find_by(id: params[:id]) attachment = Attachment.find_by(id: params[:id])
dir_path = "#{Rails.root}/public/preview" dir_path = "#{Rails.root}/public/preview"
Dir.mkdir(dir_path) unless Dir.exist?(dir_path) Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
if params[:status] == "preview" if params[:status] == "preview"
if system("cp -r #{absolute_path(local_path(attachment))} #{dir_path}/") if system("cp -r #{absolute_path(local_path(attachment))} #{dir_path}/")
render json: {status: 1, url: "/preview/#{attachment.disk_filename}"} render json: {status: 1, url: "/preview/#{attachment.disk_filename}"}
else else
normal_status(-1, "出现错误,请稍后重试") normal_status(-1, "出现错误,请稍后重试")
end end
else else
if system("rm -rf #{dir_path}/#{attachment.disk_filename}") if system("rm -rf #{dir_path}/#{attachment.disk_filename}")
normal_status(1, "操作成功") normal_status(1, "操作成功")
else else
normal_status(-1, "出现错误,请稍后重试") normal_status(-1, "出现错误,请稍后重试")
end end
end end
end end
private private
def find_file def find_file
@file = @file =
if params[:type] == 'history' if params[:type] == 'history'
AttachmentHistory.find params[:id] AttachmentHistory.find params[:id]
else else
Attachment.find params[:id] Attachment.find params[:id]
end end
end end
def delete_file(file_path) def delete_file(file_path)
File.delete(file_path) if File.exist?(file_path) File.delete(file_path) if File.exist?(file_path)
end end
def current_month_folder def current_month_folder
date = Time.now date = Time.now
"#{date.year}/#{date.month.to_s.rjust(2, '0')}" "#{date.year}/#{date.month.to_s.rjust(2, '0')}"
end end
def file_ext(file_name) def file_ext(file_name)
ext = '' ext = ''
exts = file_name.split(".") exts = file_name.split(".")
if exts.size > 1 if exts.size > 1
ext = ".#{exts.last}" ext = ".#{exts.last}"
end end
ext ext
end end
def file_save_to_local(save_path, temp_file, ext) def file_save_to_local(save_path, temp_file, ext)
unless Dir.exists?(save_path) unless Dir.exists?(save_path)
FileUtils.mkdir_p(save_path) ##不成功这里会抛异常 FileUtils.mkdir_p(save_path) ##不成功这里会抛异常
end end
digest = md5_file(temp_file) digest = md5_file(temp_file)
digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}" digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}"
local_file_path = File.join(save_path, digest) + ext local_file_path = File.join(save_path, digest) + ext
save_temp_file(temp_file, local_file_path) save_temp_file(temp_file, local_file_path)
[local_file_path, digest] [local_file_path, digest]
end end
def save_temp_file(temp_file, save_file_path) def save_temp_file(temp_file, save_file_path)
File.open(save_file_path, 'wb') do |f| File.open(save_file_path, 'wb') do |f|
temp_file.rewind temp_file.rewind
while (buffer = temp_file.read(8192)) while (buffer = temp_file.read(8192))
f.write(buffer) f.write(buffer)
end end
end end
end end
def md5_file(temp_file) def md5_file(temp_file)
md5 = Digest::MD5.new md5 = Digest::MD5.new
temp_file.rewind temp_file.rewind
while (buffer = temp_file.read(8192)) while (buffer = temp_file.read(8192))
md5.update(buffer) md5.update(buffer)
end end
md5.hexdigest md5.hexdigest
end end
def file_save_to_ucloud(path, file, content_type) def file_save_to_ucloud(path, file, content_type)
ufile = Gitlink::Ufile.new( ufile = Gitlink::Ufile.new(
ucloud_public_key: edu_setting('public_key'), ucloud_public_key: edu_setting('public_key'),
ucloud_private_key: edu_setting('private_key'), ucloud_private_key: edu_setting('private_key'),
ucloud_public_read: true, ucloud_public_read: true,
ucloud_public_bucket: edu_setting('public_bucket'), ucloud_public_bucket: edu_setting('public_bucket'),
ucloud_public_bucket_host: edu_setting('public_bucket_host'), ucloud_public_bucket_host: edu_setting('public_bucket_host'),
ucloud_public_cdn_host: edu_setting('public_cdn_host'), ucloud_public_cdn_host: edu_setting('public_cdn_host'),
) )
File.open(file) do |f| File.open(file) do |f|
ufile.put(path, f, 'Content-Type' => content_type) ufile.put(path, f, 'Content-Type' => content_type)
end end
edu_setting('public_cdn_host') + "/" + path edu_setting('public_cdn_host') + "/" + path
end end
def attachment_candown def attachment_candown
unless current_user.admin? || current_user.business? unless current_user.admin? || current_user.business?
candown = true candown = true
unless params[:type] == 'history' if @file.container
if @file.container && current_user.logged? if @file.container.is_a?(Issue)
if @file.container.is_a?(Issue) project = @file.container.project
course = @file.container.project candown = project.is_public || (current_user.logged? && project.member?(current_user))
candown = course.member?(current_user) || course.is_public elsif @file.container.is_a?(Journal)
elsif @file.container.is_a?(Journal) project = @file.container.issue.project
course = @file.container.issue.project candown = project.is_public || (current_user.logged? && project.member?(current_user))
candown = course.member?(current_user) else
else project = nil
course = nil end
end tip_exception(403, "您没有权限进入") if project.present? && !candown
tip_exception(403, "您没有权限进入") if course.present? && !candown end
tip_exception(403, "您没有权限进入") if @file.container.is_a?(ApplyUserAuthentication) end
end end
end
end def send_file_with_range(path, options = {})
end logger.info("########request.headers: #{request.headers}")
logger.info("########request.headers: #{File.exist?(path)}")
def send_file_with_range(path, options = {})
logger.info("########request.headers: #{request.headers}") if File.exist?(path)
logger.info("########request.headers: #{File.exist?(path)}") size = File.size(path)
logger.info("########request.headers: #{request.headers}")
if File.exist?(path) if !request.headers["Range"]
size = File.size(path) status_code = 200 # 200 OK
logger.info("########request.headers: #{request.headers}") offset = 0
if !request.headers["Range"] length = File.size(path)
status_code = 200 # 200 OK else
offset = 0 status_code = 206 # 206 Partial Content
length = File.size(path) bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
else offset = bytes.begin
status_code = 206 # 206 Partial Content length = bytes.end - bytes.begin
bytes = Rack::Utils.byte_ranges(request.headers, size)[0] end
offset = bytes.begin response.header["Accept-Ranges"] = "bytes"
length = bytes.end - bytes.begin response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes
end response.header["status"] = status_code
response.header["Accept-Ranges"] = "bytes"
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes send_data IO.binread(path, length, offset), options
response.header["status"] = status_code else
raise ActionController::MissingFile, "Cannot read file #{path}."
send_data IO.binread(path, length, offset), options end
else end
raise ActionController::MissingFile, "Cannot read file #{path}."
end end
end
end

View File

@ -1,35 +1,19 @@
class BindUsersController < ApplicationController class BindUsersController < ApplicationController
# before_action :require_login
def create def create
# user = CreateBindUserService.call(create_params) Rails.logger.debug "--------------开始绑定用户------------"
# Rails.logger.debug "--------------params: #{params.to_unsafe_h}"
if params[:type] == "qq" tip_exception '系统错误' if session[:unionid].blank?
begin
user = CreateBindUserService.call(current_user, create_params)
successful_authentication(user) if user.id != current_user.id
render_ok bind_user = User.try_to_login(params[:username], params[:password])
rescue ApplicationService::Error => ex tip_exception '用户名或者密码错误' if bind_user.blank?
render_error(ex.message) tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s)
end tip_exception '参数错误' unless ["qq", "wechat", "gitee", "github", "educoder"].include?(params[:type].to_s)
else tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s)
begin
tip_exception '系统错误' if session[:unionid].blank?
bind_user = User.try_to_login(params[:username], params[:password]) "OpenUsers::#{params[:type].to_s.capitalize}".constantize.create!(user: bind_user, uid: session[:unionid])
tip_exception '用户名或者密码错误' if bind_user.blank? successful_authentication(bind_user)
tip_exception '用户名或者密码错误' unless bind_user.check_password?(params[:password].to_s) @user = bind_user
tip_exception '该账号已被绑定,请更换其他账号进行绑定' if bind_user.bind_open_user?(params[:type].to_s)
OpenUsers::Wechat.create!(user: bind_user, uid: session[:unionid])
successful_authentication(bind_user)
render_ok
rescue Exception => e
render_error(e.message)
end
end
end end
def new_user def new_user

View File

@ -14,12 +14,12 @@ class Ci::CloudAccountsController < Ci::BaseController
def create def create
flag, msg = check_bind_cloud_account! flag, msg = check_bind_cloud_account!
return render_error(msg) if flag === true return tip_exception(msg) if flag === true
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@cloud_account = bind_account! @cloud_account = bind_account!
if @cloud_account.blank? if @cloud_account.blank?
render_error('激活失败, 请检查你的云服务器信息是否正确.') tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
else else
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
@ -27,17 +27,17 @@ class Ci::CloudAccountsController < Ci::BaseController
end end
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def activate def activate
return render_error('请先认证') unless current_user.ci_certification? return tip_exception('请先认证') unless current_user.ci_certification?
begin begin
@cloud_account = Ci::CloudAccount.find params[:id] @cloud_account = Ci::CloudAccount.find params[:id]
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
if @repo if @repo
return render_error('该项目已经激活') if @repo.repo_active? return tip_exception('该项目已经激活') if @repo.repo_active?
@repo.activate!(@project) @repo.activate!(@project)
else else
@repo = Ci::Repo.auto_create!(@ci_user, @project) @repo = Ci::Repo.auto_create!(@ci_user, @project)
@ -50,7 +50,7 @@ class Ci::CloudAccountsController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
end end
@ -59,39 +59,39 @@ class Ci::CloudAccountsController < Ci::BaseController
def bind def bind
flag, msg = check_bind_cloud_account! flag, msg = check_bind_cloud_account!
return render_error(msg) if flag === true return tip_exception(msg) if flag === true
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@cloud_account = bind_account! @cloud_account = bind_account!
if @cloud_account.blank? if @cloud_account.blank?
render_error('激活失败, 请检查你的云服务器信息是否正确.') tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
else else
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
end end
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def trustie_bind def trustie_bind
account = params[:account].to_s 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! 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 ActiveRecord::Base.transaction do
@cloud_account = trustie_bind_account! @cloud_account = trustie_bind_account!
if @cloud_account.blank? if @cloud_account.blank?
render_error('激活失败, 请检查你的云服务器信息是否正确.') tip_exception('激活失败, 请检查你的云服务器信息是否正确.')
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
else else
current_user.set_drone_step!(User::DEVOPS_UNVERIFIED) current_user.set_drone_step!(User::DEVOPS_UNVERIFIED)
end end
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def unbind def unbind
@ -107,18 +107,18 @@ class Ci::CloudAccountsController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def oauth_grant def oauth_grant
password = params[:password].to_s 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 oauth = current_user.oauths.last
return render_error("服务器出小差了.") if oauth.blank? return tip_exception("服务器出小差了.") if oauth.blank?
result = gitea_oauth_grant!(password, oauth) 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) current_user.set_drone_step!(User::DEVOPS_CERTIFICATION)
end end

View File

@ -30,7 +30,7 @@ class Ci::PipelinesController < Ci::BaseController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
size = Ci::Pipeline.where('branch=? and identifier=? and owner=?', params[:branch], params[:repo], params[:owner]).size size = Ci::Pipeline.where('branch=? and identifier=? and owner=?', params[:branch], params[:repo], params[:owner]).size
if size > 0 if size > 0
render_error("#{params[:branch]}分支已经存在流水线!") tip_exception("#{params[:branch]}分支已经存在流水线!")
return return
end end
pipeline = Ci::Pipeline.new(pipeline_name: params[:pipeline_name], file_name: params[:file_name],owner: params[:owner], 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}) render_ok({id: pipeline.id})
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
# 在代码库创建文件 # 在代码库创建文件
@ -81,6 +81,7 @@ class Ci::PipelinesController < Ci::BaseController
repo_branch: pipeline.branch, repo_branch: pipeline.branch,
repo_config: pipeline.file_name repo_config: pipeline.file_name
} }
Rails.logger.info("########create_params===#{create_params.to_json}")
repo = Ci::Repo.create_repo(create_params) repo = Ci::Repo.create_repo(create_params)
repo repo
end end
@ -118,7 +119,7 @@ class Ci::PipelinesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def destroy def destroy
@ -132,7 +133,7 @@ class Ci::PipelinesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def content def content
@ -182,7 +183,7 @@ class Ci::PipelinesController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def update_stage def update_stage
@ -192,7 +193,7 @@ class Ci::PipelinesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def delete_stage def delete_stage
@ -205,7 +206,7 @@ class Ci::PipelinesController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def update_stage_index(pipeline_id, show_index, diff) def update_stage_index(pipeline_id, show_index, diff)
@ -229,7 +230,7 @@ class Ci::PipelinesController < Ci::BaseController
unless steps.empty? unless steps.empty?
steps.each do |step| steps.each do |step|
unless step[:template_id] unless step[:template_id]
render_error('请选择模板!') tip_exception('请选择模板!')
return return
end end
if !step[:id] if !step[:id]
@ -246,7 +247,7 @@ class Ci::PipelinesController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def create_stage_step def create_stage_step
@ -262,7 +263,7 @@ class Ci::PipelinesController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def update_stage_step def update_stage_step
@ -279,7 +280,7 @@ class Ci::PipelinesController < Ci::BaseController
render_ok render_ok
end end
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def delete_stage_step def delete_stage_step
@ -289,6 +290,6 @@ class Ci::PipelinesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
end end

View File

@ -30,19 +30,19 @@ class Ci::ProjectsController < Ci::BaseController
@file = interactor.result @file = interactor.result
render_result(1, "更新成功") render_result(1, "更新成功")
else else
render_error(interactor.error) tip_exception(interactor.error)
end end
end end
def activate def activate
return render_error('你还未认证') unless current_user.ci_certification? return tip_exception('你还未认证') unless current_user.ci_certification?
begin begin
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
if @repo 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) @repo.activate!(@project)
return render_ok
else else
@repo = Ci::Repo.auto_create!(@ci_user, @project) @repo = Ci::Repo.auto_create!(@ci_user, @project)
@ci_user.update_column(:user_syncing, false) @ci_user.update_column(:user_syncing, false)
@ -55,12 +55,12 @@ class Ci::ProjectsController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
end end
def deactivate def deactivate
return render_error('该项目已经取消激活') if !@repo.repo_active? return tip_exception('该项目已经取消激活') if !@repo.repo_active?
@project.update_column(:open_devops, false) @project.update_column(:open_devops, false)
@repo.deactivate_repos! @repo.deactivate_repos!

View File

@ -20,14 +20,14 @@ class Ci::SecretsController < Ci::BaseController
if result["id"] if result["id"]
render_ok render_ok
else else
render_error(result["message"]) tip_exception(result["message"])
end end
else else
result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret result = Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], options).create_secret
if result["id"] if result["id"]
render_ok render_ok
else else
render_error(result["message"]) tip_exception(result["message"])
end end
end 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 Ci::Drone::API.new(@ci_user.user_hash, ci_drone_url, params[:owner], params[:repo], {name: name}).delete_secret
render_ok render_ok
else else
render_error("参数名不能为空") tip_exception("参数名不能为空")
end end
rescue Exception => ex rescue Exception => ex
render_ok render_ok

View File

@ -50,7 +50,7 @@ class Ci::TemplatesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def update def update
@ -63,7 +63,7 @@ class Ci::TemplatesController < Ci::BaseController
) )
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
def destroy def destroy
@ -73,7 +73,7 @@ class Ci::TemplatesController < Ci::BaseController
end end
render_ok render_ok
rescue Exception => ex rescue Exception => ex
render_error(ex.message) tip_exception(ex.message)
end end
#======流水线模板查询=====# #======流水线模板查询=====#

View File

@ -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

View File

@ -6,9 +6,14 @@ class CompareController < ApplicationController
end end
def show def show
load_compare_params if params[:type] == "sha"
compare load_compare_params
@merge_status, @merge_message = get_merge_message @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_size = page_size <= 0 ? 1 : page_size
@page_limit = page_limit <=0 ? 15 : page_limit @page_limit = page_limit <=0 ? 15 : page_limit
@page_offset = (@page_size -1) * @page_limit @page_offset = (@page_size -1) * @page_limit
@ -58,7 +63,7 @@ class CompareController < ApplicationController
Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, Addressable::URI.escape(base), Addressable::URI.escape(head), current_user.gitea_token) Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, Addressable::URI.escape(base), Addressable::URI.escape(head), current_user.gitea_token)
end end
def page_size def page_size
params.fetch(:page, 1).to_i params.fetch(:page, 1).to_i
end end

View File

@ -18,15 +18,15 @@ module Acceleratorable
end end
def accelerator_domain def accelerator_domain
Gitea.gitea_config[:accelerator]["domain"] GiteaService.gitea_config[:accelerator]["domain"]
end end
def accelerator_username def accelerator_username
Gitea.gitea_config[:accelerator]["access_key_id"] GiteaService.gitea_config[:accelerator]["access_key_id"]
end end
def config_accelerator? def config_accelerator?
Gitea.gitea_config[:accelerator].present? GiteaService.gitea_config[:accelerator].present?
end end
def is_foreign_url?(clone_addr) def is_foreign_url?(clone_addr)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -160,9 +160,9 @@ module Ci::CloudAccountManageable
state = SecureRandom.hex(8) state = SecureRandom.hex(8)
# redirect_uri eg: # 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 # 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") # redirect_uri = CGI.escape("#{@cloud_account.drone_url}/login")
clientId = client_id(oauth) # 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}" grant_url = "#{@cloud_account.drone_url}/login"
logger.info "[gitea] grant_url: #{grant_url}" logger.info "[gitea] grant_url: #{grant_url}"
conn = Faraday.new(url: grant_url) do |req| conn = Faraday.new(url: grant_url) do |req|
@ -179,7 +179,7 @@ module Ci::CloudAccountManageable
def drone_oauth_user!(url, state) def drone_oauth_user!(url, state)
logger.info "[drone] drone_oauth_user url: #{url}" 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.request :url_encoded
req.adapter Faraday.default_adapter req.adapter Faraday.default_adapter
req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}" req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}"
@ -188,8 +188,8 @@ module Ci::CloudAccountManageable
response = conn.get response = conn.get
logger.info "[drone] response headers: #{response.headers}" logger.info "[drone] response headers: #{response.headers}"
true # true
# response.headers['location'].include?('error') ? false : true response.headers['location'].include?('error') ? false : true
end end
private private

View File

@ -11,7 +11,7 @@ module LoginHelper
def set_autologin_cookie(user) def set_autologin_cookie(user)
token = Token.get_or_create_permanent_login_token(user, "autologin") 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}" Rails.logger.info "###### def set_autologin_cookie and get_or_create_permanent_login_token result: #{token&.value}"
cookie_options = { cookie_options = {
@ -44,6 +44,7 @@ module LoginHelper
set_autologin_cookie(user) set_autologin_cookie(user)
UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip) UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip)
# user.daily_reward
user.update_column(:last_login_on, Time.now) user.update_column(:last_login_on, Time.now)
# 注册完成后有一天的试用申请(先去掉) # 注册完成后有一天的试用申请(先去掉)
# UserDayCertification.create(user_id: user.id, status: 1) # UserDayCertification.create(user_id: user.id, status: 1)
@ -116,6 +117,7 @@ module LoginHelper
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash)) interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash))
if interactor.success? if interactor.success?
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########" Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########"
user.update_column(:is_sync_pwd, true)
true true
else else
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea fail!: #{interactor.error}" Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea fail!: #{interactor.error}"

View File

@ -1,21 +1,25 @@
module RegisterHelper module RegisterHelper
extend ActiveSupport::Concern extend ActiveSupport::Concern
def autologin_register(username, email, password, platform= 'forge', need_edit_info = false) def autologin_register(username, email, password, platform = 'forge', phone = nil, nickname =nil, need_edit_info = false)
result = {message: nil, user: nil} result = {message: nil, user: nil}
email = email.blank? ? "#{username}@example.org" : email
user = User.new(admin: false, login: username, mail: email, type: "User") user = User.new(admin: false, login: username, mail: email, type: "User")
user.password = password user.password = password
user.platform = platform user.platform = platform
user.phone = phone if phone.present?
user.nickname = nickname if nickname.present?
if need_edit_info if need_edit_info
user.need_edit_info user.need_edit_info
else else
user.activate user.activate
end end
return unless user.valid? return unless user.valid?
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
result ={}
if interactor.success? if interactor.success?
gitea_user = interactor.result gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.call(username, password) result = Gitea::User::GenerateTokenService.call(username, password)
@ -26,7 +30,7 @@ module RegisterHelper
result[:user] = {id: user.id, token: user.gitea_token} result[:user] = {id: user.id, token: user.gitea_token}
end end
else else
result[:message] = interactor.error result[:message] = interactor.result[:message]
end end
result result
end end
@ -58,4 +62,32 @@ module RegisterHelper
end end
end end
def auto_update(user, params={})
return if params.blank?
result = {message: nil, user: nil}
before_login = user.login
user.login = params[:username]
user.password = params[:password]
user.mail = params[:email]
if user.save!
sync_params = {
password: params[:password].to_s,
email: params[:email],
login_name: params[:username],
new_name: params[:username],
source_id: 0
}
interactor = Gitea::User::UpdateInteractor.call(before_login, sync_params)
if interactor.success?
result[:user] = user
else
result[:message] = '用户同步Gitea失败!'
end
else
result[:message] = user.errors.full_messages.join(",")
return
end
end
end end

View File

@ -3,8 +3,8 @@ module RenderHelper
render json: { status: 0, message: 'success' }.merge(data) render json: { status: 0, message: 'success' }.merge(data)
end end
def render_error(message = '') def render_error(message = '', status = -1)
render json: { status: -1, message: message } render json: { status: status, message: message }
end end
def render_not_acceptable(message = '请求已拒绝') def render_not_acceptable(message = '请求已拒绝')

View File

@ -5,7 +5,17 @@ module Repository::LanguagesPercentagable
result = Gitea::Repository::Languages::ListService.call(@owner.login, result = Gitea::Repository::Languages::ListService.call(@owner.login,
@repository.identifier, current_user&.gitea_token) @repository.identifier, current_user&.gitea_token)
result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil @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
end end
# hash eq:{"JavaScript": 301681522,"Ruby": 1444004,"Roff": 578781} # hash eq:{"JavaScript": 301681522,"Ruby": 1444004,"Roff": 578781}

View File

@ -3,9 +3,12 @@ class ForksController < ApplicationController
before_action :require_profile_completed, only: [:create] before_action :require_profile_completed, only: [:create]
before_action :load_project before_action :load_project
before_action :authenticate_project!, :authenticate_user! before_action :authenticate_project!, :authenticate_user!
skip_after_action :user_trace_log, only: [:create]
def create def create
@new_project = Projects::ForkService.new(current_user, @project, params[:organization]).call @new_project = Projects::ForkService.new(current_user, @project, params[:organization]).call
user = current_user
Rails.logger.user_trace.info("{id: #{user.id}, login: #{user.login}, url: #{request.url}, method: #{request.method}, params: #{params.merge(forkee: @new_project.id)}, response_code: #{response.code}, time: #{Time.now}}")
end end
private private

View File

@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController
def index 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)) @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
@page = params[:page] || 1 @page = params[:page] || 1
@limit = params[:limit] || 15 @limit = params[:limit] || 15

View File

@ -11,6 +11,8 @@ class IssuesController < ApplicationController
before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue] before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue]
before_action :check_token_enough, :find_atme_receivers, only: [:create, :update] before_action :check_token_enough, :find_atme_receivers, only: [:create, :update]
skip_after_action :user_trace_log, only: [:update]
include ApplicationHelper include ApplicationHelper
include TagChosenHelper include TagChosenHelper
@ -237,6 +239,7 @@ class IssuesController < ApplicationController
# end # end
# end # end
# end # end
issue_hash = old_value_to_hash(@issue, params)
if @issue.issue_type.to_s == "2" && params[:status_id].to_i == 5 && @issue.author_id != current_user.try(:id) if @issue.issue_type.to_s == "2" && params[:status_id].to_i == 5 && @issue.author_id != current_user.try(:id)
normal_status(-1, "不允许修改为关闭状态") normal_status(-1, "不允许修改为关闭状态")
@ -244,6 +247,9 @@ class IssuesController < ApplicationController
issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id)
Issues::UpdateForm.new({subject: issue_params[:subject], description: issue_params[:description].blank? ? issue_params[:description] : issue_params[:description].b}).validate! Issues::UpdateForm.new({subject: issue_params[:subject], description: issue_params[:description].blank? ? issue_params[:description] : issue_params[:description].b}).validate!
if @issue.update_attributes(issue_params) if @issue.update_attributes(issue_params)
user_trace_update_log(issue_hash)
if @issue&.pull_request.present? if @issue&.pull_request.present?
SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @issue&.pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @issue&.pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @issue&.pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu? SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @issue&.pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu?

View File

@ -4,6 +4,7 @@ class JournalsController < ApplicationController
before_action :set_issue before_action :set_issue
before_action :check_issue_permission before_action :check_issue_permission
before_action :set_journal, only: [:destroy, :edit, :update] before_action :set_journal, only: [:destroy, :edit, :update]
skip_after_action :user_trace_log, only: [:update]
def index def index
@page = params[:page] || 1 @page = params[:page] || 1
@ -86,9 +87,11 @@ class JournalsController < ApplicationController
def update def update
content = params[:content] content = params[:content]
if content.present? if content.present?
old_value = old_value_to_hash(@journal, params)
Journals::UpdateForm.new({notes: notes.to_s.strip.blank? ? notes.to_s.strip : notes.to_s.strip.b}).validate! Journals::UpdateForm.new({notes: notes.to_s.strip.blank? ? notes.to_s.strip : notes.to_s.strip.b}).validate!
if @journal.update_attribute(:notes, content) if @journal.update_attribute(:notes, content)
user_trace_update_log(old_value)
normal_status(0, "更新成功") normal_status(0, "更新成功")
else else
normal_status(-1, "更新失败") normal_status(-1, "更新失败")

View File

@ -0,0 +1,19 @@
class LogController < ApplicationController
def list
path = "#{Rails.root}/log"
@file_list = []
Dir.foreach(path) do |file|
@file_list << file
end
@file_list = @file_list.sort
end
def download
path = "#{Rails.root}/log/#{params[:filename]}"
if params[:filename] && File.exist?(path) && File.file?(path)
send_file(path, filename: params[:filename])
else
render json: { message: 'no such file!' }
end
end
end

View File

@ -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

View File

@ -6,6 +6,7 @@ class MembersController < ApplicationController
before_action :operate! before_action :operate!
before_action :check_member_exists!, only: %i[create] before_action :check_member_exists!, only: %i[create]
before_action :check_member_not_exists!, only: %i[remove change_role] before_action :check_member_not_exists!, only: %i[remove change_role]
skip_after_action :user_trace_log, only: [:change_role]
def create def create
interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user) interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user)
@ -26,6 +27,9 @@ class MembersController < ApplicationController
@total_count = scope.size @total_count = scope.size
@members = paginate(scope) @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 end
def remove def remove
@ -39,7 +43,9 @@ class MembersController < ApplicationController
end end
def change_role def change_role
old_value = @project.members.where(user_id: params[:user_id])[0].roles.last.name
interactor = Projects::ChangeMemberRoleInteractor.call(@project.owner, @project, @user, params[:role]) interactor = Projects::ChangeMemberRoleInteractor.call(@project.owner, @project, @user, params[:role])
user_trace_update_log(old_value)
SendTemplateMessageJob.perform_later('ProjectRole', current_user.id, @user.id, @project.id, message_role_name) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('ProjectRole', current_user.id, @user.id, @project.id, message_role_name) if Site.has_notice_menu?
render_response(interactor) render_response(interactor)
rescue Exception => e rescue Exception => e
@ -61,11 +67,14 @@ class MembersController < ApplicationController
end end
def check_member_exists! 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 end
def check_member_not_exists! 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 end
def check_user_profile_completed def check_user_profile_completed

View File

@ -1,7 +1,7 @@
class NoticesController < ApplicationController class NoticesController < ApplicationController
def create def create
tip_exception("参数有误") if params["source"].blank? return tip_exception("参数有误") if params["source"].blank?
user_id = params[:user_id] user_id = params[:user_id]
if params["source"] == "CompetitionBegin" if params["source"] == "CompetitionBegin"
@ -13,9 +13,21 @@ class NoticesController < ApplicationController
elsif params["source"] == "CompetitionReview" elsif params["source"] == "CompetitionReview"
competition_id = params[:competition_id] competition_id = params[:competition_id]
SendTemplateMessageJob.perform_later('CompetitionReview', user_id, 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 else
tip_exception("#{params["source"]}未配置") tip_exception("#{params["source"]}未配置")
end end
render_ok render_ok
end end
private
def params_props
params.require(:notice).permit(:props)
end
end end

View File

@ -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

View File

@ -3,6 +3,7 @@ class Oauth::BaseController < ActionController::Base
include LoginHelper include LoginHelper
include ControllerRescueHandler include ControllerRescueHandler
include LoggerHelper include LoggerHelper
include RegisterHelper
# include LaboratoryHelper # include LaboratoryHelper
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
@ -13,13 +14,13 @@ class Oauth::BaseController < ActionController::Base
private private
def tip_exception(status = -1, message) def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message) raise Gitlink::TipException.new(status, message)
end end
def tip_show_exception(status = -2, message) def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message) raise Gitlink::TipException.new(status, message)
end end
def tip_show(exception) def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}") uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json render json: exception.tip_json
@ -35,7 +36,7 @@ class Oauth::BaseController < ActionController::Base
end end
def auth_hash 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'] request.env['omniauth.auth']
end end

View File

@ -0,0 +1,93 @@
class Oauth::CallbacksController < Oauth::BaseController
def create
process_callback_new
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
def process_callback_new
Rails.logger.info("[OAuth2] omniauth.auth -> #{request.env['omniauth.auth'].inspect}")
if auth_hash.blank?
redirect_to("/login") && return
end
platform = auth_hash[:provider]
uid = auth_hash[:uid]
uid = auth_hash.info.unionid if platform == "wechat"
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)
redirect_to root_path(new_user: false)
return
else
if current_user.blank? || !current_user.logged?
session[:unionid] = uid
else
"OpenUsers::#{platform.to_s.capitalize}".constantize.create!(user: current_user, uid: uid)
end
end
Rails.logger.info("[OAuth2] session[:unionid] -> #{session[:unionid]}")
redirect_to "/bindlogin/#{platform}"
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

View File

@ -0,0 +1,39 @@
class Oauth2Controller < ActionController::Base
layout 'doorkeeper/application'
include LoginHelper
def show
client_id = params[:call_url].split("client_id=")[1].split("&redirect_uri")[0]
@call_url = request.fullpath.split('call_url=').last
@app = Doorkeeper::Application.find_by(uid: client_id)
end
def create
if params[:login].blank?
@error = {msg: '邮箱地址或用户名不能为空', id: 'login'}
elsif params[:password].blank?
@error = {msg: '请输入密码', id: 'password'}
else
@user = User.try_to_login(params[:login], params[:password])
return @error = {msg: '账号或密码错误', id: 'login'} if @user.blank?
return @error = {msg: '违反平台使用规范,账号已被锁定', id: 'login'} if @user.locked?
login_control = LimitForbidControl::UserLogin.new(@user)
return @error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'} if login_control.forbid?
password_ok = @user.check_password?(params[:password].to_s)
unless password_ok
if login_control.remain_times-1 == 0
@error = {msg: "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码", id: 'account'}
else
@error = {msg: "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会", id: 'account'}
end
login_control.increment!
return
end
login_control.clear
redirect_to params[:call_url] + "&auth=" + @user.login
end
end
end

View File

@ -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

View File

@ -4,12 +4,14 @@ class Organizations::OrganizationUsersController < Organizations::BaseController
def index def index
@organization_users = @organization.organization_users.includes(:user) @organization_users = @organization.organization_users.includes(:user)
search = params[:search].to_s.downcase if params[:search].present?
user_condition_users = User.like(search).to_sql search = params[:search].to_s.downcase
team_condition_teams = User.joins(:teams).merge(@organization.teams.like(search)).to_sql user_condition_users = User.like(search).to_sql
users = User.from("( #{user_condition_users} UNION #{team_condition_teams }) AS users") 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
@organization_users = @organization_users.where(user_id: users).distinct
end
@organization_users = kaminari_paginate(@organization_users) @organization_users = kaminari_paginate(@organization_users)
end end

View File

@ -31,6 +31,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate! Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate!
@organization = Organizations::CreateService.call(current_user, organization_params) @organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present? Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
Cache::V2::OwnerCommonService.new(@organization.id).reset
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
@ -45,9 +46,16 @@ class Organizations::OrganizationsController < Organizations::BaseController
@organization.nickname = organization_params[:nickname] if organization_params[:nickname].present? @organization.nickname = organization_params[:nickname] if organization_params[:nickname].present?
@organization.save! @organization.save!
sync_organization_extension! sync_organization_extension!
# 更改组织可见性为私有,则需将该组织下的所有仓库同步更改为私有仓库
if organization_extension_params[:visibility] == "privacy"
Project.where(user_id: @organization.id).where(is_public: true).each do |project|
update_project_private(project)
end
end
Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload) Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present? Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
Cache::V2::OwnerCommonService.new(@organization.id).reset
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
@ -121,5 +129,20 @@ class Organizations::OrganizationsController < Organizations::BaseController
def sync_organization_extension! def sync_organization_extension!
@organization.organization_extension.update_attributes!(organization_extension_params) @organization.organization_extension.update_attributes!(organization_extension_params)
end end
def update_project_private(project)
project.update_attributes!(is_public: false)
project.forked_projects.update_all(is_public: project.is_public)
gitea_params = {
private: true,
default_branch: project.default_branch,
website: project.website,
name: project.identifier
}
gitea_repo = Gitea::Repository::UpdateService.call(@organization, project&.repository&.identifier, gitea_params)
project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]})
# 更新对应所属分类下的项目数量(私有)
project.project_category.decrement!(:private_projects_count, 1) if project.project_category.present?
end
end end

View File

@ -21,6 +21,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
tip_exception(e.message) tip_exception(e.message)
end 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 def destroy
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@ -33,6 +44,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
tip_exception(e.message) tip_exception(e.message)
end 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 private
def load_organization def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id]) @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 end
def load_operate_project 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? tip_exception("项目不存在") if @operate_project.nil?
end end

View File

@ -12,7 +12,7 @@ class OwnersController < ApplicationController
def show def show
@owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id]) @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id])
return render_ok(type: 'User') unless @owner.present? return render_not_found unless @owner.present?
# 组织 # 组织
if @owner.is_a?(Organization) if @owner.is_a?(Organization)
return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition

View File

@ -10,7 +10,7 @@ class ProjectCategoriesController < ApplicationController
end end
def group_list 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 # projects = Project.no_anomory_projects.visible
# @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size # @category_group_list = projects.joins(:project_category).group("project_categories.id", "project_categories.name").size
end end

View File

@ -1,10 +1,10 @@
class ProjectRankController < ApplicationController class ProjectRankController < ApplicationController
# 根据时间获取热门项目 # 根据时间获取热门项目
def index def index
$redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names) $redis_cache.zunionstore("recent-days-project-rank-#{time}", get_timeable_key_names)
deleted_data = $redis_cache.smembers("v2-project-rank-deleted") deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
$redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank? $redis_cache.zrem("recent-days-project-rank-#{time}", deleted_data) unless deleted_data.blank?
@project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true) @project_rank = $redis_cache.zrevrange("recent-days-project-rank-#{time}", 0, 9, withscores: true)
rescue Exception => e rescue Exception => e
@project_rank = [] @project_rank = []
end end

View File

@ -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

View File

@ -9,7 +9,7 @@ class Projects::WebhooksController < Projects::BaseController
def create def create
ActiveRecord::Base.transaction do 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? return render_error("参数错误.") unless webhook_params.present?
form = Projects::Webhooks::CreateForm.new(webhook_params) form = Projects::Webhooks::CreateForm.new(webhook_params)
return render json: {status: -1, message: form.errors} unless form.validate! return render json: {status: -1, message: form.errors} unless form.validate!

View File

@ -22,6 +22,7 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge? menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge?
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions") menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge? menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge?
menu.append(menu_hash_by_name("services")) if @project.has_menu_permission("services") && @project.forge? && (current_user.admin? || @project.member?(current_user.id))
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge? menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge?
menu.append(menu_hash_by_name("activity")) menu.append(menu_hash_by_name("activity"))
menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge? menu.append(menu_hash_by_name("settings")) if user_is_admin && @project.forge?
@ -39,8 +40,9 @@ class ProjectsController < ApplicationController
category_id = params[:category_id] category_id = params[:category_id]
@total_count = @total_count =
if category_id.blank? if category_id.blank?
ps = ProjectStatistic.first # ps = ProjectStatistic.first
ps.common_projects_count + ps.mirror_projects_count unless ps.blank? # ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
@projects.total_count
else else
cate = ProjectCategory.find_by(id: category_id) cate = ProjectCategory.find_by(id: category_id)
cate&.projects_count || 0 cate&.projects_count || 0
@ -51,7 +53,7 @@ class ProjectsController < ApplicationController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
Projects::CreateForm.new(project_params).validate! Projects::CreateForm.new(project_params).validate!
@project = Projects::CreateService.new(current_user, project_params).call @project = Projects::CreateService.new(current_user, project_params).call
OpenProjectDevOpsJob.perform_later(@project&.id, current_user.id)
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
@ -153,6 +155,15 @@ class ProjectsController < ApplicationController
} }
gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params) gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params)
@project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]}) @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 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? 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 end
@ -170,6 +181,8 @@ class ProjectsController < ApplicationController
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
@project.destroy! @project.destroy!
@project.forked_projects.update_all(forked_from_project_id: nil) @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 render_ok
end end
else else

View File

@ -1,5 +1,5 @@
class PublicKeysController < ApplicationController class PublicKeysController < ApplicationController
before_action :require_login before_action :require_login_cloud_ide_saas
before_action :find_public_key, only: [:destroy] before_action :find_public_key, only: [:destroy]
def index def index
@ -61,4 +61,6 @@ class PublicKeysController < ApplicationController
def find_public_key def find_public_key
@public_key = current_user.public_keys.find_by_id(params[:id]) @public_key = current_user.public_keys.find_by_id(params[:id])
end end
end end

View File

@ -6,6 +6,8 @@ class PullRequestsController < ApplicationController
before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits] before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits]
before_action :load_pull_request, only: [:files, :commits] before_action :load_pull_request, only: [:files, :commits]
before_action :find_atme_receivers, only: [:create, :update] before_action :find_atme_receivers, only: [:create, :update]
skip_after_action :user_trace_log, only: [:update]
include TagChosenHelper include TagChosenHelper
include ApplicationHelper include ApplicationHelper
@ -56,12 +58,19 @@ class PullRequestsController < ApplicationController
end end
def create def create
# return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user) if params[:fork_project_id].present?
fork_project= Project.find_by(id: params[:fork_project_id])
return normal_status(-1, "您不是源项目开发者,没有权限,请联系源项目管理员.") unless fork_project && fork_project.operator?(current_user)
else
return normal_status(-1, "您不是项目开发者,没有权限,请联系项目管理员.") unless @project.operator?(current_user)
end
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
Issues::CreateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! Issues::CreateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate!
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params) @pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
if @gitea_pull_request[:status] == :success if @gitea_pull_request[:status] == :success
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"]) @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('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? 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)}" Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
@ -103,22 +112,16 @@ class PullRequestsController < ApplicationController
Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! Issues::UpdateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate!
merge_params merge_params
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? reviewers = User.where(id: params[:reviewer_ids])
if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? @pull_request.reviewers = reviewers
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
return normal_status(-1, "最多只能创建一个标记。") old_issue_value = old_value_to_hash(@issue, @issue_params)
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1 old_pr_value = old_value_to_hash(@pull_request, @local_params.compact)
@issue&.issue_tags_relates&.destroy_all old_value = {issue: old_issue_value, pull_request: old_pr_value}
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
if @issue.update_attributes(@issue_params) if @issue.update_attributes(@issue_params)
if @pull_request.update_attributes(@local_params.compact) if @pull_request.update_attributes(@local_params.compact)
user_trace_update_log(old_value)
gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier, gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier,
@pull_request.gitea_number, @requests_params, current_user.gitea_token) @pull_request.gitea_number, @requests_params, current_user.gitea_token)
@ -133,7 +136,7 @@ class PullRequestsController < ApplicationController
end end
else else
return normal_status(-1, "请输入正确的标记。") return normal_status(-1, "请输入正确的标记。")
end end
end end
if params[:status_id].to_i == 5 if params[:status_id].to_i == 5
@issue.issue_times.update_all(end_time: Time.now) @issue.issue_times.update_all(end_time: Time.now)
@ -163,8 +166,6 @@ class PullRequestsController < ApplicationController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user) colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
<<<<<<< HEAD
colsed === true ? normal_status(1, "已拒绝") : normal_status(-1, '合并失败')
# author: zxh # author: zxh
# 调用上链API # 调用上链API
success_blockchain = push_activity_2_blockchain("pull_request_refuse", @pull_request) success_blockchain = push_activity_2_blockchain("pull_request_refuse", @pull_request)
@ -172,14 +173,14 @@ class PullRequestsController < ApplicationController
normal_status(-1, "拒绝失败") normal_status(-1, "拒绝失败")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
else else
======= if colsed === true
if colsed === true
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE) @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? SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "已拒绝") normal_status(1, "已拒绝")
else else
normal_status(-1, '合并失败') normal_status(-1, '合并失败')
>>>>>>> upstream/master
end end
rescue => e rescue => e
normal_status(-1, e.message) normal_status(-1, e.message)
@ -197,6 +198,7 @@ class PullRequestsController < ApplicationController
@issue_assign_to = @issue.get_assign_user @issue_assign_to = @issue.get_assign_user
@gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login,
@repository.identifier, @pull_request.gitea_number, current_user&.gitea_token) @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
@last_review = @pull_request.reviews.take
end end
def pr_merge def pr_merge
@ -220,7 +222,6 @@ class PullRequestsController < ApplicationController
# @pull_request.project_trend_status! # @pull_request.project_trend_status!
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE) @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
@issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id) @issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
<<<<<<< HEAD
# author: zxh # author: zxh
# 调用上链API # 调用上链API
@ -267,10 +268,11 @@ class PullRequestsController < ApplicationController
end end
end end
end end
=======
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "合并成功") normal_status(1, "合并成功")
>>>>>>> upstream/master
else else
normal_status(-1, result.message) normal_status(-1, result.message)
end end
@ -332,7 +334,7 @@ class PullRequestsController < ApplicationController
def get_relatived def get_relatived
@project_tags = @project.issue_tags&.select(:id,:name, :color).as_json @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_members = @project.all_developers
@project_priories = IssuePriority&.select(:id,:name, :position).as_json @project_priories = IssuePriority&.select(:id,:name, :position).as_json
end end

View File

@ -1,390 +1,407 @@
class RepositoriesController < ApplicationController class RepositoriesController < ApplicationController
include RepositoriesHelper include RepositoriesHelper
include ApplicationHelper include ApplicationHelper
include OperateProjectAbilityAble include OperateProjectAbilityAble
include Repository::LanguagesPercentagable include Repository::LanguagesPercentagable
before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror] before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
before_action :require_profile_completed, only: [:create_file] before_action :require_profile_completed, only: [:create_file]
before_action :load_repository before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive] before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror] 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_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts] before_action :get_statistics, only: %i[top_counts]
def files def files
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token) result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
render json: result render json: result
end end
# 新版项目详情 # 新版项目详情
def detail def detail
@user = current_user @user = current_user
@result = Repositories::DetailService.call(@owner, @repository, @user) @result = Repositories::DetailService.call(@owner, @repository, @user)
@project_fork_id = @project.try(:forked_from_project_id) @project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present? if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id) @fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner @fork_project_user = @fork_project.owner
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)
end end
def show def show
@user = current_user @user = current_user
@repo = @project.repository @repo = @project.repository
@result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil @result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil
@project_fork_id = @project.try(:forked_from_project_id) @project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present? if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id) @fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner @fork_project_user = @fork_project.owner
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)
end end
def entries def entries
@project.increment!(:visits) @week_project_visit_record, @month_project_visit_record = TimeableVisitRecord.build(@project.id)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id) if @week_project_visit_record.visits < 300 && @month_project_visit_record.visits < 1000
if @project.educoder? @week_project_visit_record.increment!(:visits)
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name) @month_project_visit_record.increment!(:visits)
else @project.increment!(:visits)
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
@entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] end
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" if @project.educoder?
end @entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
end else
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
def top_counts @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
@result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
end end
end
def sub_entries
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) def top_counts
@result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call
if @project.educoder? end
if params[:type] === 'file'
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri) def sub_entries
logger.info "######### sub_entries: #{@sub_entries}" file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
if @project.educoder?
tmp_entries = { if params[:type] === 'file'
"content" => @sub_entries['data']['content'], @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
"type" => "blob" logger.info "######### sub_entries: #{@sub_entries}"
} return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
@sub_entries = {
"trees"=>tmp_entries, tmp_entries = {
"commits" => [{}] "content" => @sub_entries['data']['content'],
} "type" => "blob"
else }
begin @sub_entries = {
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri}) "trees"=>tmp_entries,
if @sub_entries.blank? || @sub_entries['status'].to_i === -1 "commits" => [{}]
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri) }
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1 else
tmp_entries = { begin
"content" => @sub_entries['data']['content'], @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
"type" => "blob" if @sub_entries.blank? || @sub_entries['status'].to_i === -1
} @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
@sub_entries = { return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
"trees"=>tmp_entries, tmp_entries = {
"commits" => [{}] "content" => @sub_entries['data']['content'],
} "type" => "blob"
end }
rescue @sub_entries = {
return render_error('该文件暂未开放,敬请期待.') "trees"=>tmp_entries,
end "commits" => [{}]
end }
else end
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" rescue
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref) return render_error('该文件暂未开放,敬请期待.')
if interactor.success? end
result = interactor.result end
@sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result else
else @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
render_error(interactor.error) interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
end if interactor.success?
end result = interactor.result
end @sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
else
def commits render_error(interactor.error)
if @project.educoder? end
@commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name) end
else end
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) def commits
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri, if @project.educoder?
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call @commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
else else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, if params[:filepath].present?
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
end @hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
end sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
def commits_slice sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
@hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier, end
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token) end
end end
def commit def commits_slice
@sha = params[:sha] @hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
if @project.educoder? sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
return render_error('暂未开放,敬请期待.') end
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token) def commit
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true}) @sha = params[:sha]
end if @project.educoder?
end return render_error('暂未开放,敬请期待.')
else
def tags @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]}) @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)
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result end
end end
def contributors def tags
if params[:filepath].present? || @project.educoder? if params[:only_name].present?
@contributors = [] result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
else
result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) @tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
@contributors = result.is_a?(Hash) && result.key?(:status) ? [] : result else
end name_result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
rescue
@contributors = [] @tag_names = result.is_a?(Hash) && result.key?(:status) ? [] : name_result
end
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
def edit
return render_forbidden if !@project.manager?(current_user) && !current_user.admin? @tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
end end
end
def create_file
interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params) def contributors
if interactor.success? if params[:filepath].present? || @project.educoder?
@file = interactor.result @contributors = []
# create_new_pr(params) else
#如果是更新流水线文件 result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
if params[:pipeline_id] @contributors = result.is_a?(Hash) && result.key?(:status) ? [] : result
update_pipeline(params[:pipeline_id]) end
end rescue
else @contributors = []
render_error(interactor.error) end
end
end def edit
return render_forbidden if !@project.manager?(current_user) && !current_user.admin?
def update_pipeline(pipeline_id) end
pipeline = Ci::Pipeline.find(pipeline_id)
if pipeline def create_file
pipeline.update!(sync: 1) interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params)
end if interactor.success?
end @file = interactor.result
# create_new_pr(params)
def update_file #如果是更新流水线文件
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier)) if params[:pipeline_id]
if interactor.success? update_pipeline(params[:pipeline_id])
@file = interactor.result end
# TODO: 是否创建pr else
# create_new_pr(params) render_error(interactor.error)
render_result(1, "更新成功") end
else end
render_error(interactor.error)
end def update_pipeline(pipeline_id)
end pipeline = Ci::Pipeline.find(pipeline_id)
if pipeline
def delete_file pipeline.update!(sync: 1)
interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier)) end
if interactor.success? end
@file = interactor.result
render_result(1, "文件删除成功") def update_file
else interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
render_error(interactor.error) if interactor.success?
end @file = interactor.result
end # TODO: 是否创建pr
# create_new_pr(params)
def repo_hook render_result(1, "更新成功")
else
end render_error(interactor.error)
end
def sync_mirror end
return render_error("正在镜像中..") if @repository.mirror.waiting?
def delete_file
@repository.sync_mirror! interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
SyncMirroredRepositoryJob.perform_later(@repository.id, current_user.id) if interactor.success?
render_ok @file = interactor.result
end render_result(1, "文件删除成功")
else
def readme render_error(interactor.error)
if params[:filepath].present? end
result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token) end
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) def repo_hook
end
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" end
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) def sync_mirror
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") return render_error("正在镜像中..") if @repository.mirror.waiting?
rescue
render json: nil @repository.sync_mirror!
end SyncMirroredRepositoryJob.perform_later(@repository.id, current_user.id)
render_ok
def languages end
if @project.educoder?
render json: {} def readme
else if params[:filepath].present?
render json: languages_precentagable result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
end else
end result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
def archive @path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
domain = Gitea.gitea_config[:domain] @readme = result[:status] === :success ? result[:body] : nil
api_url = Gitea.gitea_config[:base_url] @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}" @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
file_path = [domain, api_url, archive_url].join rescue
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden? render json: nil
end
return render_not_found if !request.format.zip? && !request.format.gzip?
def languages
redirect_to file_path if @project.educoder?
end render json: {}
else
def raw render json: languages_precentagable
domain = Gitea.gitea_config[:domain] end
api_url = Gitea.gitea_config[:base_url] end
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}" def archive
file_path = [domain, api_url, url].join domain = GiteaService.gitea_config[:domain]
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
redirect_to file_path
end file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
private
return render_not_found if !request.format.zip? && !request.format.gzip?
def find_project
@project = Project.find params[:id] redirect_to file_path
render_not_found("未找到相关的仓库") unless @project end
end
def raw
def find_project_with_includes domain = GiteaService.gitea_config[:domain]
@project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id] api_url = GiteaService.gitea_config[:base_url]
end
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}"
def authorizate! file_path = [domain, api_url, url].join
return if current_user && current_user.admin? file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
if @project.repository.hidden? && !@project.member?(current_user)
render_forbidden redirect_to file_path
end end
end
private
# TODO 获取最新commit信息
def project_commits def find_project
if params[:filepath].present? @project = Project.find params[:id]
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) render_not_found("未找到相关的仓库") unless @project
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri, end
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
else def find_project_with_includes
Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, @project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id]
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call end
end
end def authorizate!
return if current_user && current_user.admin?
def get_statistics if @project.repository.hidden? && !@project.member?(current_user)
@branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size render_forbidden
@tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size end
end end
def get_ref # TODO 获取最新commit信息
@ref = params[:ref] || @project&.default_branch def project_commits
end if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
def get_latest_commit Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
latest_commit = @project.educoder? ? nil : project_commits sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
@latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil else
@commits_count = latest_commit.present? ? latest_commit[:total_count] : 0 Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
end sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
end
def content_params end
{
filepath: params[:filepath], def get_statistics
branch: params[:branch], @branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size
new_branch: params[:new_branch], @tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size
content: params[:content], end
message: params[:message],
committer: { def get_ref
email: current_user.mail, @ref = params[:ref] || @project&.default_branch
name: current_user.login end
},
identifier: @project.identifier def get_latest_commit
} latest_commit = @project.educoder? ? nil : project_commits
end @latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil
@commits_count = latest_commit.present? ? latest_commit[:total_count] : 0
def hook_params(hook_type, params) end
# if hook_type == "push"
# # TODO hook返回的记录中暂时没有文件代码数量的增减暂时根据 commits数量来计算 def content_params
# uploadPushInfo = { {
# "sha": params["commits"].present? ? params["commits"].last : "", filepath: params[:filepath],
# "branch": params["ref"].to_s.split("/").last, branch: params[:branch],
# "modification_lines": params["commits"].length new_branch: params[:new_branch],
# } content: params[:content],
# elsif hook_type == "pull_request" && params["action"].to_s == "closed" #合并请求合并后才会有上链操作 message: params[:message],
# uploadPushInfo = { committer: {
# "branch": params["base"]["ref"].to_s.split("/").last, email: current_user.mail,
# "sha": params["pull_request"]["merge_base"], name: current_user.login
# "modification_lines": 1 #pull_request中没有commits数量 },
# } identifier: @project.identifier
# else }
# uploadPushInfo = {} end
# end
def hook_params(hook_type, params)
# uploadPushInfo # if hook_type == "push"
end # # TODO hook返回的记录中暂时没有文件代码数量的增减暂时根据 commits数量来计算
# uploadPushInfo = {
def create_new_pr(params) # "sha": params["commits"].present? ? params["commits"].last : "",
if params[:new_branch].present? && params[:new_branch] != params[:branch] # "branch": params["ref"].to_s.split("/").last,
local_params = { # "modification_lines": params["commits"].length
title: params[:message], #标题 # }
body: params[:content], #内容 # elsif hook_type == "pull_request" && params["action"].to_s == "closed" #合并请求合并后才会有上链操作
head: params[:new_branch], #源分支 # uploadPushInfo = {
base: params[:branch], #目标分支 # "branch": params["base"]["ref"].to_s.split("/").last,
milestone: 0 #里程碑,未与本地的里程碑关联 # "sha": params["pull_request"]["merge_base"],
# "modification_lines": 1 #pull_request中没有commits数量
} # }
requests_params = local_params.merge({ # else
assignee: current_user.try(:login), # uploadPushInfo = {}
assignees: [], # end
labels: [],
due_date: Time.now # uploadPushInfo
}) end
issue_params = { def create_new_pr(params)
author_id: current_user.id, if params[:new_branch].present? && params[:new_branch] != params[:branch]
project_id: @project.id, local_params = {
subject: params[:message], title: params[:message], #标题
description: params[:content], body: params[:content], #内容
assigned_to_id: nil, head: params[:new_branch], #源分支
fixed_version_id: nil, base: params[:branch], #目标分支
issue_tags_value: nil, milestone: 0 #里程碑,未与本地的里程碑关联
issue_classify: "pull_request",
issue_type: "1", }
tracker_id: 2, requests_params = local_params.merge({
status_id: 1, assignee: current_user.try(:login),
priority_id: params[:priority_id] || "2" assignees: [],
} labels: [],
@pull_issue = Issue.new(issue_params) due_date: Time.now
if @pull_issue.save! })
local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id))
if local_requests.save issue_params = {
gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call author_id: current_user.id,
if gitea_request[:status] == :success && local_requests.update_attributes(gpid: gitea_request["body"]["number"]) project_id: @project.id,
local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") subject: params[:message],
end description: params[:content],
end assigned_to_id: nil,
end fixed_version_id: nil,
end issue_tags_value: nil,
end issue_classify: "pull_request",
issue_type: "1",
end tracker_id: 2,
status_id: 1,
priority_id: params[:priority_id] || "2"
}
@pull_issue = Issue.new(issue_params)
if @pull_issue.save!
local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id))
if local_requests.save
gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call
if gitea_request[:status] == :success && local_requests.update_attributes(gpid: gitea_request["body"]["number"])
local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
end
end
end
end
end
end

View File

@ -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

View File

@ -1,24 +1,26 @@
class SettingsController < ApplicationController class SettingsController < ApplicationController
def show def show
@old_projects_url = nil @old_projects_url = nil
@old_projects_url = "https://www.trustie.net/users/#{current_user.try(:login)}/projects" if User.current.logged?
get_navbar get_navbar
get_add_menu get_add_menu
get_common_menu get_common_menu
get_sub_competitions get_sub_competitions
get_personal_menu get_personal_menu
get_third_party get_third_party
get_third_party_new
get_top_system_notification get_top_system_notification
end end
private private
def get_navbar def get_navbar
@navbar = default_laboratory.navbar @navbar = default_laboratory.navbar
if User.current.logged? if User.current.logged?
pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "#{Rails.application.config_for(:configuration)['platform_url']}/current_user"), "hidden"=>false} pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "#{Rails.application.config_for(:configuration)['platform_url']}/current_user"), "hidden"=>false}
@navbar << pernal_index @navbar << pernal_index
end end
end end
def get_add_menu def get_add_menu
@add = [] @add = []
Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site| Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site|
@ -67,7 +69,25 @@ class SettingsController < ApplicationController
url: EducoderOauth.oauth_url url: EducoderOauth.oauth_url
} }
end end
def get_third_party_new
@third_party_new = []
@third_party_new << {
name: 'educoder',
url: EducoderOauth.oauth_url,
method: 'get'
}
platform_url = Rails.application.config_for(:configuration)['platform_url']
config = Rails.application.config_for(:configuration)
(config.dig("oauth").keys - ["educoder", "wechat"]).each do |provider|
@third_party_new << {
name: provider,
url: "#{platform_url}/auth/#{provider}",
method: 'get'
}
end
end
def get_top_system_notification def get_top_system_notification
@top_system_notification = SystemNotification.is_top.first @top_system_notification = SystemNotification.is_top.first
end end

View File

@ -0,0 +1,90 @@
class SponsorTiersController < ApplicationController
before_action :set_sponsor_tier, only: [:show, :edit, :update, :destroy]
before_action :check_sponsor, only: [:show]
before_action :require_login, only: [:create, :update, :destroy]
# GET /sponsor_tiers
# GET /sponsor_tiers.json
def index
# @sponsor_tiers = SponsorTier.all
user = User.find_by_login(params[:login])
@sponsor_tiers = user.sponsor_tier
end
# GET /sponsor_tiers/1
# GET /sponsor_tiers/1.json
def show
end
# POST /sponsor_tiers
# POST /sponsor_tiers.json
def create
# print("------------\n", sponsor_tier_params, "\n------------\n")
@check_sponsorship = nil
@sponsor_tier = SponsorTier.new(sponsor_tier_params)
respond_to do |format|
if @sponsor_tier.user_id == User.current.id && @sponsor_tier.save
format.html { redirect_to @sponsor_tier, notice: 'Sponsor tier was successfully created.' }
format.json { render :show, status: :created, location: @sponsor_tier }
# render json: {status: 1, message: '创建成功' }
else
format.html { render :new }
format.json { render json: @sponsor_tier.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /sponsor_tiers/1
# PATCH/PUT /sponsor_tiers/1.json
def update
@check_sponsorship = nil
old_value = old_value_to_hash(@sponsor_tier, params)
respond_to do |format|
if User.current.id == @sponsor_tier.user_id && @sponsor_tier.update(sponsor_tier_update_params)
user_trace_update_log(old_value)
format.html { redirect_to @sponsor_tier, notice: 'Sponsor tier was successfully updated.' }
format.json { render :show, status: :ok, location: @sponsor_tier }
# render json: {status: 1, message: '修改成功' }
else
format.html { render :edit }
format.json { render json: @sponsor_tier.errors, status: :unprocessable_entity }
# format.json { render status: :unprocessable_entity }
# render json: {status: -1, message: '修改失败' }
end
end
end
# DELETE /sponsor_tiers/1
# DELETE /sponsor_tiers/1.json
def destroy
if User.current.id == @sponsor_tier.user_id
@sponsor_tier.destroy
respond_to do |format|
format.html { redirect_to sponsor_tiers_url, notice: 'Sponsor tier was successfully destroyed.' }
format.json { head :no_content }
end
else
format.json { render json: @sponsor_tier.errors, status: :unprocessable_entity }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def check_sponsor
@check_sponsorship = Sponsorship.where("sponsor_id=? AND developer_id=?", current_user.id, @sponsor_tier.user.id)
end
def set_sponsor_tier
@sponsor_tier = SponsorTier.find(params[:id])
end
def sponsor_tier_update_params
params.require(:sponsor_tier).permit(:tier, :description)
end
# Only allow a list of trusted parameters through.
def sponsor_tier_params
params.require(:sponsor_tier).permit(:tier, :user_id, :description)
end
end

Some files were not shown because too many files have changed in this diff Show More