Compare commits

..

110 Commits

Author SHA1 Message Date
xiaoxiaoqiong fccaa476e4 educoder修改邮箱和密码同步 2021-12-17 10:46:13 +08:00
yystopf 807e91a839 fix 2021-11-05 17:37:55 +08:00
yystopf 13497ae6dc add: setting of engineer_url 2021-11-05 17:05:28 +08:00
jasder 9955619398 Merge pull request 'FIX WIP' (#221) from jasder/forgeplus:dev_educoder into dev_educoder 2021-10-23 16:10:32 +08:00
jasder ae2a2725f7 FIX conflict 2021-10-23 16:09:35 +08:00
jasder 6220d851ae FIX 合并主干分支代码以及解决代码冲突 2021-10-23 16:06:49 +08:00
yystopf d2e18d9c0c fix merge from develop 2021-09-18 10:43:35 +08:00
jasder 039ca21f30 Merge pull request 'FIX merge develop' (#144) from develop into dev_educoder 2021-09-09 09:44:44 +08:00
jasder cbc74e4709 Merge pull request 'Fix 路由规范化' (#141) from jasder/forgeplus:dev_educoder into dev_educoder 2021-09-06 14:18:33 +08:00
jasder fd4806012d FIX 合并develop主干代码(平台路由规范化) 2021-09-06 14:16:38 +08:00
jasder 4825c910f0 FIX merge develop conflict 2021-08-27 16:12:14 +08:00
viletyy ace13abe92 fix: 合并请求对比代码返回合并信息 2021-07-12 18:45:35 +08:00
jasder e8da6364d8 Merge branch 'develop' into dev_educoder 2021-07-12 16:16:59 +08:00
jasder a7e20dcae2 FIX 解决用户同步问题 2021-07-09 17:53:14 +08:00
jasder 06e24a8ea4 Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-07-09 16:58:04 +08:00
jasder 070df3c9dd FIX 解决私有版本库库不能访问、下载图片的问题 2021-07-09 16:57:37 +08:00
viletyy 7c0bc5d444 add: create project owner login 2021-07-07 15:51:05 +08:00
jasder 6ebab1be66 FIX delete gantt、sonar tab 2021-07-07 09:13:40 +08:00
jasder 27f510e6c9 ADD loger info for create gitea user method 2021-07-07 00:07:01 +08:00
jasder d9a27b7728 ADD gantt, sonar menu for menu_list api 2021-07-06 15:49:26 +08:00
jasder 68f1d7f7f6 ADD 项目tab中添加 甘特图和质量分析/检测标识 2021-07-06 15:41:18 +08:00
jasder 7e3517d873 FIX show realname for create project 2021-07-06 11:04:25 +08:00
jasder 877ef54b24 FIX bug 2021-07-06 10:43:55 +08:00
jasder 133eed6afe FIX 合并代码、重构优化gitea 用户注册api 2021-07-05 16:19:23 +08:00
jasder eba2df5a2d Merge branch 'educoder_server' into dev_educoder 2021-07-05 15:21:23 +08:00
jasder 85a37fdbcc Merge branch 'feature/pull_conflict_files' into dev_educoder 2021-06-28 10:05:01 +08:00
jasder a62b29a263 Merge branch 'feature/pull_conflict_files' into dev_educoder 2021-06-28 10:00:10 +08:00
jasder 0eccafcb69 Merge branch 'feature/pull_conflict_files' into dev_educoder 2021-06-28 09:39:39 +08:00
jasder 0dbed862a7 Merge branch 'develop' into dev_educoder 2021-06-27 21:46:35 +08:00
jasder dfc998a9cc Merge branch 'develop' into dev_educoder 2021-06-18 17:47:59 +08:00
jasder 8be4bc823c Merge branch 'develop' into dev_educoder 2021-06-17 17:31:17 +08:00
jasder 7af6d8d754 FIX merge flick 2021-06-17 17:10:16 +08:00
jasder 5a8dc9d5e7 ADD merge develop branch 2021-06-17 14:09:59 +08:00
jasder ecdb79ee48 fix code bug 2021-06-11 17:40:55 +08:00
jasder 50e0ad9804 Merge branch 'dev_educoder' into educoder_server 2021-06-11 17:36:53 +08:00
jasder 953d6790d0 FIX create gitea user bug 2021-06-11 17:35:27 +08:00
jasder 136d1c1abd Merge branch 'educoder_server' of https://git.trustie.net/jasder/forgeplus into educoder_server 2021-06-11 16:51:30 +08:00
jasder e085ef374b Merge branch 'develop' into dev_educoder 2021-05-24 10:41:05 +08:00
jasder a557139ad0 Merge branch 'develop' into dev_educoder 2021-05-17 17:15:08 +08:00
jasder 6d0ac60d5a ADD phone query params for get list users api 2021-05-17 16:58:42 +08:00
jasder dda1868a72 Merge branch 'develop' into dev_educoder 2021-05-17 16:57:57 +08:00
jasder 93a1a08393 FIX merge develop branch adn fix confict 2021-05-17 16:26:51 +08:00
yystopf 4d4d8d1369 项目模块项目id重定向
项目模块项目id重定向
2021-05-03 19:50:21 +08:00
vilet.yy ab42fb7476 add: projects redirect to 2021-05-03 19:40:30 +08:00
jasder 11ac2f9c9d FIX delete log 2021-04-30 16:16:08 +08:00
jasder 8a9c3df2e5 Merge branch 'educoder_server' into dev_educoder 2021-04-30 16:09:40 +08:00
jasder e90f80255e FIX 完善项目添加成员流程 2021-04-30 16:09:02 +08:00
jasder 1b6c082480 fix merge develop 2021-04-07 11:40:29 +08:00
jasder ce1152cd4f Merge branch 'educoder_server' into dev_educoder 2021-04-02 15:54:07 +08:00
jasder c8a9094ad0 Merge branch 'develop' into dev_educoder 2021-04-02 15:53:48 +08:00
jasder 569088d846 Merge branch 'dev_educoder' into educoder_server 2021-04-01 21:00:41 +08:00
jasder 6159cda155 Merge branch 'develop' into dev_educoder 2021-04-01 21:00:22 +08:00
jasder d738ea432b Merge branch 'dev_educoder' into educoder_server 2021-04-01 15:08:32 +08:00
jasder 4a30e8c83f FIX conflict 2021-04-01 15:08:29 +08:00
jasder b3288937c9 fix merge 2021-04-01 15:07:12 +08:00
jasder 7520fa03c2 Merge branch 'develop' into dev_educoder 2021-03-28 20:05:52 +08:00
jasder f282d971e3 FIX get branches api bug and project menu list bug 2021-03-28 19:56:11 +08:00
jasder 56f01801ae Merge branch 'dev_educoder' into educoder_server 2021-03-28 19:45:24 +08:00
jasder 45e70367d5 Merge branch 'develop' into dev_educoder 2021-03-28 19:45:07 +08:00
jasder a0b376e70a Merge branch 'dev_educoder' into educoder_server 2021-03-27 23:55:11 +08:00
jasder d62d757c42 Merge branch 'develop' into dev_educoder 2021-03-27 23:54:55 +08:00
jasder 033c2607e2 Merge branch 'dev_educoder' into educoder_server 2021-03-25 21:30:46 +08:00
vilet.yy 0d6abdf7d0 fix: setting common arr 2021-03-25 20:59:24 +08:00
jasder 22c68f4c70 FIX bug 2021-03-25 20:29:36 +08:00
jasder fcd86fb01b Merge branch 'dev_educoder' into educoder_server 2021-03-25 19:58:28 +08:00
jasder faecb99136 Merge branch 'develop' into dev_educoder 2021-03-25 19:58:03 +08:00
jasder 15a002f601 Update defalut laboratory setting url 2021-03-23 16:36:35 +08:00
jasder 206d7bc159 Merge branch 'trustie_server' into dev_educoder 2021-03-23 15:58:47 +08:00
jasder 5e0006a830 Merge branch 'trustie_server' into dev_educoder 2021-03-23 15:47:13 +08:00
jasder 3abf993463 Merge branch 'trustie_server' into dev_educoder 2021-03-23 15:43:08 +08:00
jasder efbf6d627d FIX merge 2021-03-23 11:43:24 +08:00
jasder 6836907146 FIX code bug 2021-03-23 10:26:20 +08:00
jasder 6c4058cde5 Merge branch 'educoder_server' into dev_educoder 2021-03-23 10:22:12 +08:00
jasder 5a2e17abe2 FIX project and project_info aasion 2021-03-23 09:34:20 +08:00
jasder 9a8b89bcd8 FIX 解决实训作业关联项目的问题 2021-03-23 09:28:40 +08:00
jasder 4856da1607 Merge branch 'dev_educoder' into educoder_server 2021-03-22 21:02:56 +08:00
jasder 883a5f7e38 Merge branch 'develop' into dev_educoder 2021-03-22 21:02:35 +08:00
jasder 1658c1b923 Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-22 17:58:29 +08:00
jasder 74f6a54d29 FIX update setting api for nav bar url 2021-03-22 17:58:15 +08:00
vilet.yy adb315521e fix 2021-03-22 14:20:03 +08:00
vilet.yy 01cad866b8 fix: laboratory 500
fix

fix
2021-03-22 14:17:03 +08:00
jasder 3447b17cdf FIX change password bug 2021-03-22 14:13:25 +08:00
jasder 0079255e98 Merge branch 'develop' into dev_educoder 2021-03-21 11:25:57 +08:00
vilet.yy 490bff5ded fix: form error 2021-03-19 16:18:09 +08:00
vilet.yy 008b8ac709 Merge branch 'develop' into dev_educoder 2021-03-19 15:07:34 +08:00
vilet.yy d0532a8f70 Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-19 11:54:10 +08:00
vilet.yy a8c4cde350 add: user info sync_pwd 2021-03-19 11:50:35 +08:00
jasder 5de5974102 FIX remove email for gitea_register api 2021-03-19 10:54:56 +08:00
jasder 3d3e778078 Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-19 10:31:12 +08:00
jasder f5ea8dc4a2 FIX change password api 2021-03-19 10:31:06 +08:00
vilet.yy c4b942a5f2 fix 2021-03-18 18:22:07 +08:00
jasder 13391b189a FIX conflict 2021-03-18 18:17:41 +08:00
jasder b116a72ce7 Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-18 18:15:22 +08:00
jasder f01aec94d6 FIX 2021-03-18 18:15:08 +08:00
jasder cc97f85a1e ADD has_ansenty 2021-03-18 18:11:06 +08:00
vilet.yy 133995080a add: user info email 2021-03-18 18:11:06 +08:00
vilet.yy 27304c35dc add: user info mark 2021-03-18 18:11:06 +08:00
vilet.yy d6bf85ef73 add: user gitea register 2021-03-18 18:11:06 +08:00
jasder be9c01bf1d ADD educoder api 2021-03-18 18:11:06 +08:00
jasder edeaaa5db7 FIX sync user for educoder 2021-03-18 18:11:06 +08:00
jasder 0e41b6cc9b FIX 整理表结构 2021-03-18 18:11:03 +08:00
jasder 5f3946f187 ADD has_ansenty 2021-03-18 18:07:56 +08:00
jasder b7be00f3ec Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-18 17:28:20 +08:00
jasder 9b21e29426 ADD educoder api 2021-03-18 17:27:18 +08:00
vilet.yy f37fccfdb3 add: user info email 2021-03-18 17:22:59 +08:00
vilet.yy 810d2dbac6 add: user info mark 2021-03-18 16:00:05 +08:00
vilet.yy 6af97d865f Merge branch 'dev_educoder' of https://git.trustie.net/jasder/forgeplus into dev_educoder 2021-03-18 15:28:09 +08:00
vilet.yy efba4b2b4f add: user gitea register 2021-03-18 15:28:03 +08:00
jasder aaf1f43cc2 FIX sync user for educoder 2021-03-18 13:46:19 +08:00
jasder 823c99bd71 FIX 整理表结构 2021-03-15 16:02:59 +08:00
1357 changed files with 26840 additions and 114155 deletions

2
.gitignore vendored
View File

@ -37,11 +37,13 @@ public/react/yarn.lock
# Ignore react node_modules # Ignore react node_modules
public/system/* public/system/*
public/react/*
/public/react/.cache /public/react/.cache
/public/react/node_modules/ /public/react/node_modules/
/public/react/config/stats.json /public/react/config/stats.json
/public/react/stats.json /public/react/stats.json
/public/react/.idea/* /public/react/.idea/*
/public/react/build/*
/public/h5build /public/h5build
/public/npm-debug.log /public/npm-debug.log

View File

@ -23,7 +23,7 @@
* Fix 版本库中附件下载400(#51625) * Fix 版本库中附件下载400(#51625)
* Fix loading页面优化(#51588) * Fix loading页面优化(#51588)
* Fix 提交详情页面优化(#51577) * Fix 提交详情页面优化(#51577)
* Fix 修复修复制功能(#51569) * Fix 修复修复制功能(#51569)
* Fix 修复新建发行版用户信息显示错误的问题(#51665) * Fix 修复新建发行版用户信息显示错误的问题(#51665)
* Fix 修复查看文件详细信息报错的问题(#51561) * Fix 修复查看文件详细信息报错的问题(#51561)
* Fix 修复提交记录中时间显示格式问题(#51526) * Fix 修复提交记录中时间显示格式问题(#51526)
@ -62,7 +62,7 @@
## [v3.0.3](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-08 ## [v3.0.3](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-08
* BUGFIXES * BUGFIXES
* Fix 解决修标题过长导致的排版问题(45469) * Fix 解决修标题过长导致的排版问题(45469)
* Fix 解决合并请求详情页面排版错误的问题(45457) * Fix 解决合并请求详情页面排版错误的问题(45457)
* FIX 解决转移仓库界面专有名词描述错误的问题(45455) * FIX 解决转移仓库界面专有名词描述错误的问题(45455)
* Fix 解决markdown格式文件自动生成数字排序的问题(45454) * Fix 解决markdown格式文件自动生成数字排序的问题(45454)

143
README.md
View File

@ -1,30 +1,17 @@
# GitLink - CCF开源创新服务平台 Trustie (确实)是一个以大众化协同开发、开放式资源共享、持续性可信评估为核心机理,面向高校创新实践的在线协作平台。
GitLink确实开源是中国计算机学会CCF官方指定的开源创新服务平台旨在以“为开源创新服务”为使命以“成为开源创新的汇聚地”为愿景秉承“创新、开放、协作、共享”的价值观致力于为大规模开源开放协同创新助力赋能打造创新成果孵化和新工科人才培养的开源创新生态 ## 特性
<center> - 软件创作与生产深度融合的软件开发环境体系结构 软件自由创作和工程生产的高效衔接,适于软件开发中群体智慧的有效汇聚。
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/gitlink.png?raw=true" width=80% />
</center>
## 特色功能
- **分布式协作开发**基于Git打造分布式代码托管环境提供免费公、私有代码仓库支持在线文件编辑、代码分支管理、协作贡献统计、代码仓库复刻Fork、贡献合并请求PR、群智贡献审阅等功能让您的项目在这里健康、快速的成长 - 构件化协同开发环境的可扩展运行框架多样化工具的集成和联动,形成了强动态扩展能力的平台框架。
- **一站式过程管理**提供易修Issue、里程碑、通知提醒、标签归档等多样化任务管理工具支持各类开发任务的发布、指派与跟踪同时提供在线Wiki文档、组织多粒度管理等功能为您搭建一站式的项目过程管理环境让您的团队协作更高效、过程更透明 - “互联网即资源库”的全新软件复用模式 成长式软件资源管理系统,实现了分散资源的知识融合、资源的可持续增长和有效复用。
- **高效流水线运维**融合DevOps思想提供轻量级的工作流引擎Engine打通编码、测试、构建、部署等开发运维环节支持自定义配置、代码静态扫描、构建自动触发、容器镜像托管等功能同时支持接入第三方运维工具让您的代码更加快速、可靠地形成高质量的产品 ## 部署
- **多层次代码分析**提供软件软代码和芯片RTL代码的溯源分析、文件级和组件级许可证识别及风险分析、输入性开源漏洞检测和加固建议支持分析结果的多层次可视化展示帮助您实施有效开源治理厘清代码引用链发现并消除漏洞感染链为安全合规的开源引用保驾护航
- **多维度用户画像**:实时采集和分析平台中的各类开源资源数据,搭建多维度用户画像评估系统,提供开发活动统计、贡献度日历、用户能力建模、角色与专业定位分析等功能,让您在个人主页展示开发动态与创新能力!
## 第三方组件
## 部署流程 ### Depends Versions
### 依赖库
* Ruby 2.4.5 * Ruby 2.4.5
@ -36,19 +23,22 @@ GitLink确实开源是中国计算机学会CCF官方指定的开源
* imagemagick * imagemagick
### 步骤 ### Steps
1克隆稳定版本 #### 1. 克隆稳定版本
``` ```
git clone -b standalone https://git.trustie.net/jasder/forgeplus.git git clone -b standalone https://git.trustie.net/jasder/forgeplus.git
``` ```
2安装依赖包 #### 2. 安装依赖包
```bash ```bash
cd forgeplus && bundle install cd forgeplus && bundle install
``` ```
3配置初始化文件进入项目根目录执行以下命令 #### 3. 配置初始化文件
进入项目根目录执行一下命令:
```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
@ -56,7 +46,11 @@ touch config/redis.yml
touch config/elasticsearch.yml touch config/elasticsearch.yml
``` ```
4配置数据库数据库配置信息请查看/config/database.yml文件项目默认采用mysql数据库, 如需更改,请自行修改配置信息,默认配置如下 #### 4. 配置数据库
数据库配置信息请查看/config/database.yml文件
项目默认采用mysql数据库, 如需更改,请自行修改配置信息,
默认配置如下:
```bash ```bash
default: &default default: &default
adapter: mysql2 adapter: mysql2
@ -66,13 +60,14 @@ default: &default
password: 123456 password: 123456
``` ```
5配置gitea服务(可选)如需要部署自己的gitea平台请参考[gitea官方平台文档](https://docs.gitea.io/zh-cn/install-from-binary/)。因目前gitea平台api受限暂时推荐从forge平台获取[gitea部署文件](https://forgeplus.trustie.net/projects/Trustie/gitea-binary)进行部署 #### 5. 配置gitea服务(可选)
**如需要部署自己的gitea平台请参考gitea官方平台https://docs.gitea.io/zh-cn/install-from-binary/**
- 配置gitea服务步骤 **因目前gitea平台api受限暂时推荐从forge平台获取gitea部署文件进行部署https://forgeplus.trustie.net/projects/Trustie/gitea-binary**
-- 部署gitea服务并注册root账户 **配置gitea服务步骤**
1. 部署gitea服务并注册root账户
-- 修改forge平台的 config/configuration.yml中的gitea服务指向地址 2. 修改forge平台的 config/configuration.yml中的gitea服务指向地址
```ruby ```ruby
gitea: gitea:
@ -82,9 +77,10 @@ gitea:
base_url: '/api/v1' base_url: '/api/v1'
``` ```
6安装redis环境请自行搜索各平台如何安装部署redis环境 #### 6. 安装redis环境
**请自行搜索各平台如何安装部署redis环境**
7安装imagemagick插件 #### 7. 安装imagemagick插件
- Mac OS X - Mac OS X
```bash ```bash
brew install imagemagick ghostscript brew install imagemagick ghostscript
@ -95,95 +91,88 @@ gitea:
sudo apt-get install -y imagemagick sudo apt-get install -y imagemagick
``` ```
8创建数据库开发环境为development 生成环境为production #### 8. 创建数据库
**开发环境为development 生成环境为production**
```bash ```bash
rails db:create RAILS_ENV=development rails db:create RAILS_ENV=development
``` ```
9导入数据表结构 #### 9. 导入数据表结构
```bash ```bash
bundle exec rake sync_table_structure:import_csv bundle exec rake sync_table_structure:import_csv
``` ```
10执行migrate迁移文件开发环境为development 生成环境为production #### 10. 执行migrate迁移文件
**开发环境为development 生成环境为production**
```bash ```bash
rails db:migrate RAILS_ENV=development rails db:migrate RAILS_ENV=development
``` ```
11clone前端代码将前端代码克隆到public/react目录下目录结构应该是: public/react/build #### 11. clone前端代码
**将前端代码克隆到public/react目录下目录结构应该是: public/react/build**
```bash ```bash
git clone -b standalone https://git.trustie.net/jasder/build.git git clone -b standalone https://git.trustie.net/jasder/build.git
``` ```
12启动redis(此处以macOS系统为例) #### 12. 启动redis(此处已mac系统为例)
```bash ```bash
redis-server& redis-server&
``` ```
13启动sidekiq开发环境为development 生成环境为production #### 13. 启动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服务 #### 14. 启动rails服务
```bash ```bash
rails s rails s
``` ```
15浏览器访问在浏览器中输入如下地址访问 #### 15. 浏览器访问
在浏览器中输入如下地址访问:
```bash ```bash
http://localhost:3000/ http://localhost:3000/
``` ```
16其他说明通过页面注册以第一个用户为平台管理员用户 #### 16. 其他说明
通过页面注册都第一个用户为平台管理员用户
## 页面展示 ## 页面展示
- 项目列表 - 代码库
![](docs/figs/code.png?raw=true)
<center>
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/project_list.png?raw=true" width=50% />
</center>
- 代码仓库
<center>
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/repo.png?raw=true" width=50% />
</center>
- 任务管理 - 任务管理
![](docs/figs/issue_manage.png?raw=true)
<center>
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/issues.png?raw=true" width=50% />
</center>
- 合并请求
<center>
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/PR.png?raw=true" width=50% />
</center>
- 引擎配置
<center>
<img src="https://code.gitlink.org.cn/young/forgeplus/raw/branch/standalone/docs/figs/engine.png?raw=true" width=50% />
</center>
- 任务查看
![](docs/figs/issue_view.png?raw=true)
- 任务指派
![](docs/figs/issue_assign2.png?raw=true)
- 里程碑
![](docs/figs/milestone.png?raw=true)
### API
- [API文档](https://forgeplus.trustie.net/docs/api)
- [API](showdoc.com.cn)
账号forgeplus@admin.com 密码forge123
## 贡献代码 ## 贡献代码
我们期待您向GitLink提交贡献在您贡献时请遵循以下流程
1. Fork 项目 1. Fork 项目
2. 创建本地分支(git checkout -b my-new-feature) 2. 创建本地分支(git checkout -b my-new-feature)
3. 提交更改 (git commit -am 'Add some feature') 3. 提交更改 (git commit -am 'Add some feature')
4. 推送到分支 (git push origin my-new-feature) 4. 推送到分支 (git push origin my-new-feature)
5. 向源项目的 **develop** 分支发起 Pull Request 5. 向源项目的 **develop** 分支发起 Pull Request
#### 指导文档 ## License
- [API文档](https://www.gitlink.org.cn/docs/api)
- [Git常用命令](https://git-scm.com/)
## 许可证协议

View File

@ -51,51 +51,6 @@ http://localhost:3000/api/accounts/remote_register | jq
|-- token |string|用户token| |-- token |string|用户token|
返回值
```json
{
"status": 0,
"message": "success",
"user": {
"id": 36400,
"token": "8c87a80d9cfacc92fcb2451845104f35119eda96"
}
}
```
---
#### 独立注册接口
```
POST accounts/register
```
*示例*
```bash
curl -X POST \
-d "login=2456233122@qq.com" \
-d "password=djs_D_00001" \
-d "namespace=16895620" \
-d "code=forge" \
http://localhost:3000/api/accounts/remote_register | jq
```
*请求参数说明:*
|参数名|必选|类型|说明|
|-|-|-|-|
|login |是|string |邮箱或者手机号 |
|namespace |是|string |登录名 |
|password |是|string |密码 |
|code |是|string |验证码 |
*返回参数说明:*
|参数名|类型|说明|
|-|-|-|
|user|json object |返回数据|
|-- id |int |用户id |
|-- token |string|用户token|
返回值 返回值
```json ```json
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -99,38 +99,3 @@ $(document).on("turbolinks:before-cache", function () {
$(function () { $(function () {
}); });
$(document).on('turbolinks:load', function() {
$('.logo-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;
if (file && file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function () {
var $box = $fileInput.parent();
$box.find('img').attr('src', reader.result).css('display', 'block');
$box.addClass('has-img');
};
reader.readAsDataURL(file);
} else {
}
});
$('.attachment-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;
if (file && file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function () {
var $box = $fileInput.parent();
$box.find('img').attr('src', reader.result).css('display', 'block');
$box.addClass('has-img');
};
reader.readAsDataURL(file);
} else {
}
});
})

View File

@ -1,141 +0,0 @@
/*
* @Description: Do not edit
* @Date: 2021-08-31 11:16:45
* @LastEditors: viletyy
* @Author: viletyy
* @LastEditTime: 2021-08-31 14:19:46
* @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js
*/
$(document).on('turbolinks:load', function(){
var showSuccessNotify = function() {
$.notify({
message: '操作成功'
},{
type: 'success'
});
}
// close user
$('.project-list-container').on('click', '.recommend-action', function(){
var $closeAction = $(this);
var $uncloseAction = $closeAction.siblings('.unrecommend-action');
var $editAction = $closeAction.siblings('.edit-recommend-action');
var keywordID = $closeAction.data('id');
customConfirm({
content: '确认将该项目设置为推荐项目吗?',
ok: function(){
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
recommend: true,
recommend_index: 1
}
},
success: function() {
showSuccessNotify();
$closeAction.hide();
$uncloseAction.show();
$editAction.show();
$(".project-item-"+keywordID).children('td').eq(5).text("√")
}
});
}
});
});
// unclose user
$('.project-list-container').on('click', '.unrecommend-action', function(){
var $uncloseAction = $(this);
var $closeAction = $uncloseAction.siblings('.recommend-action');
var $editAction = $closeAction.siblings('.edit-recommend-action');
var keywordID = $uncloseAction.data('id');
customConfirm({
content: '确认取消该推荐项目吗?',
ok: function () {
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
recommend: false,
recommend_index: 0
}
},
success: function() {
showSuccessNotify();
$closeAction.show();
$uncloseAction.hide();
$editAction.hide();
$(".project-item-"+keywordID).children('td').eq(5).text("")
}
});
}
})
});
// close user
$('.project-list-container').on('click', '.pinned-action', function(){
var $closeAction = $(this);
var $uncloseAction = $closeAction.siblings('.unpinned-action');
var keywordID = $closeAction.data('id');
customConfirm({
content: '确认将该项目设置为精选项目吗?',
ok: function(){
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
is_pinned: true,
}
},
success: function() {
showSuccessNotify();
$closeAction.hide();
$uncloseAction.show();
$(".project-item-"+keywordID).children('td').eq(4).text("√")
}
});
}
});
});
// unclose user
$('.project-list-container').on('click', '.unpinned-action', function(){
var $uncloseAction = $(this);
var $closeAction = $uncloseAction.siblings('.pinned-action');
var keywordID = $uncloseAction.data('id');
customConfirm({
content: '确认取消该精选项目吗?',
ok: function () {
$.ajax({
url: '/admins/projects/' + keywordID,
method: 'PUT',
dataType: 'json',
data: {
project: {
is_pinned: false,
}
},
success: function() {
showSuccessNotify();
$closeAction.show();
$uncloseAction.hide();
$(".project-item-"+keywordID).children('td').eq(4).text("")
}
});
}
})
});
})

View File

@ -58,149 +58,3 @@ input.form-control {
position: absolute; position: absolute;
} }
.logo-item {
display: flex;
&-img {
display: block;
width: 80px;
height: 80px;
background: #e9ecef;
}
&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 80px;
height: 80px;
background: #e9ecef;
border: 1px solid #ced4da;
&::before {
content: '';
position: absolute;
top: 27px;
left: 39px;
width: 2px;
height: 26px;
background: #495057;
}
&::after {
content: '';
position: absolute;
top: 39px;
left: 27px;
width: 26px;
height: 2px;
background: #495057;
}
}
&-left {
position: relative;
width: 80px;
height: 80px;
&.has-img {
.logo-item-upload {
display: none;
}
&:hover {
.logo-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}
&-right {
display: flex;
flex-direction: column;
justify-content: space-between;
color: #777777;
font-size: 0.8rem;
}
&-title {
color: #23272B;
font-size: 1rem;
}
}
.attachment-item {
display: flex;
&-img {
display: block;
width: 160px;
height: 160px;
background: #e9ecef;
}
&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 160px;
height: 160px;
background: #e9ecef;
border: 1px solid #ced4da;
&::before {
content: '';
position: absolute;
top: 54px;
left: 78px;
width: 2px;
height: 52px;
background: #495057;
}
&::after {
content: '';
position: absolute;
top: 78px;
left: 54px;
width: 52px;
height: 2px;
background: #495057;
}
}
&-left {
position: relative;
width: 160px;
height: 160px;
&.has-img {
.attachment-item-upload {
display: none;
}
&:hover {
.attachment-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}
&-right {
padding-top: 100px;
display: flex;
flex-direction: column;
justify-content: space-between;
color: #777777;
font-size: 0.8rem;
}
&-title {
color: #23272B;
font-size: 1rem;
}
}

View File

@ -1,384 +1,390 @@
class AccountsController < ApplicationController class AccountsController < ApplicationController
include ApplicationHelper
#skip_before_action :check_account, :only => [:logout]
def index
render json: session def index
end render json: session
end
# 其他平台同步注册的用户
def remote_register # 为了同步平台上未注册gitea的用户
Register::RemoteForm.new(remote_register_params).validate! def gitea_register
username = params[:username]&.gsub(/\s+/, "") user = User.find_by(login: sync_gitea_params[:login])
tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) Users::SyncGiteaForm.new(sync_gitea_params.merge(user: user)).validate!
email = params[:email]&.gsub(/\s+/, "") return render_error("该用户已同步协作平台") if user.gitea_token.present? && user.gitea_uid.present?
password = params[:password]
platform = (params[:platform] || 'forge')&.gsub(/\s+/, "") result = create_gitea_user!(user, sync_gitea_params[:login], sync_gitea_params[:email], sync_gitea_params[:password])
ActiveRecord::Base.transaction do result[:user] ? render_ok : render_error(result[:message])
result = autologin_register(username, email, password, platform) rescue Exception => e
if result[:message].blank? uid_logger_error(e.message)
render_ok({user: result[:user]}) tip_exception(-1, e.message)
else end
render_error(result[:message])
end # 其他平台同步注册的用户
end def remote_register
rescue Exception => e username = params[:username]&.gsub(/\s+/, "")
uid_logger_error(e.message) tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.is_reversed(username).present?
tip_exception(-1, e.message) email = params[:email]&.gsub(/\s+/, "")
end password = params[:password]
platform = (params[:platform] || 'forge')&.gsub(/\s+/, "")
# 其他平台修改用户的信息,这边同步修改
def remote_update ActiveRecord::Base.transaction do
ActiveRecord::Base.transaction do result = autologin_register(username, email, password, platform)
user_params = params[:user_params] if result[:message].blank?
user_extension_params = params[:user_extension_params] render_ok({user: result[:user]})
else
u = User.find_by(login: params[:old_user_login]) render_error(result[:message])
user_mail = u.try(:mail) end
end
if u.present? rescue Exception => e
ue = u.user_extension uid_logger_error(e.message)
u.login = user_params["login"] if user_params["login"] tip_exception(-1, e.message)
u.mail = user_params["mail"] if user_params["mail"] end
u.lastname = user_params["lastname"] if user_params["lastname"]
# 其他平台修改用户的信息,这边同步修改
ue.gender = user_extension_params["gender"] def remote_update
ue.school_id = user_extension_params["school_id"] ActiveRecord::Base.transaction do
ue.location = user_extension_params["location"] user_params = params[:user_params]
ue.location_city = user_extension_params["location_city"] user_extension_params = params[:user_extension_params]
ue.identity = user_extension_params["identity"]
ue.technical_title = user_extension_params["technical_title"] u = User.find_by(login: params[:old_user_login])
ue.student_id = user_extension_params["student_id"] user_mail = u.try(:mail)
ue.description = user_extension_params["description"]
ue.save! if u.present?
u.save! ue = u.user_extension
u.login = user_params["login"] if user_params["login"]
sync_params = {} u.mail = user_params["mail"] if user_params["mail"]
u.lastname = user_params["lastname"] if user_params["lastname"]
if (user_params["mail"] && user_params["mail"] != user_mail)
sync_params = sync_params.merge(email: user_params["mail"]) ue.gender = user_extension_params["gender"]
end ue.school_id = user_extension_params["school_id"]
ue.location = user_extension_params["location"]
if sync_params.present? ue.location_city = user_extension_params["location_city"]
interactor = Gitea::User::UpdateInteractor.call(u.login, sync_params) ue.identity = user_extension_params["identity"]
if interactor.success? ue.technical_title = user_extension_params["technical_title"]
render_ok ue.student_id = user_extension_params["student_id"]
else ue.description = user_extension_params["description"]
render_error(interactor.error) ue.save!
end u.save!
end
end sync_params = {}
end
rescue Exception => e if (user_params["mail"] && user_params["mail"] != user_mail)
uid_logger_error(e.message) sync_params = sync_params.merge(email: user_params["mail"])
tip_exception(-1, e.message) end
end
if sync_params.present?
# 其他平台同步登录 interactor = Gitea::User::UpdateInteractor.call(u.login, sync_params)
def remote_login if interactor.success?
@user = User.try_to_login(params[:login], params[:password]) render_ok
if @user else
successful_authentication(@user) render_error(interactor.error)
render_ok({user: {id: @user.id, token: @user.gitea_token}}) end
else end
render_error("用户不存在") end
end end
end rescue Exception => e
uid_logger_error(e.message)
#修改密码 tip_exception(-1, e.message)
def remote_password end
@user = User.find_by(login: params[:login])
return render_error("未找到相关用户!") if @user.blank? # 其他平台同步登录
def remote_login
sync_params = { @user = User.try_to_login(params[:login], params[:password])
password: params[:password].to_s, if @user
email: @user.mail successful_authentication(@user)
} render_ok({user: {id: @user.id, token: @user.gitea_token}})
else
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) render_error("用户不存在")
if interactor.success? end
@user.update_attribute(:password, params[:password]) end
render_ok
else #修改密码
render_error(interactor.error) def remote_password
end @user = User.find_by(login: params[:login])
end return render_error("未找到相关用户!") if @user.blank?
sync_params = {
password: params[:password].to_s,
# 用户注册 email: @user.mail
# 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用 }
# params[:login] 邮箱或者手机号
# params[:namespace] 登录名 interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
# params[:code] 验证码 if interactor.success?
# code_type 1注册手机验证码 8邮箱注册验证码 @user.update_attribute(:password, params[:password])
# 本地forge注册入口需要重新更改逻辑 render_ok
def register else
# type只可能是1或者8 render_error(interactor.error)
user = nil end
begin end
Register::Form.new(register_params).validate!
user = Users::RegisterService.call(register_params)
password = register_params[:password].strip # 用户注册
# 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用
# gitea用户注册, email, username, password # params[:login] 邮箱或者手机号
interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) # params[:code] 验证码
if interactor.success? # code_type 1注册手机验证码 8邮箱注册验证码
gitea_user = interactor.result # 本地forge注册入口
result = Gitea::User::GenerateTokenService.call(user.login, password) def register
user.gitea_token = result['sha1'] begin
user.gitea_uid = gitea_user[:body]['id'] # 查询验证码是否正确;type只可能是1或者8
if user.save! type = phone_mail_type(params[:login].strip)
UserExtension.create!(user_id: user.id) # code = params[:code].strip
successful_authentication(user)
render_ok if type == 1
end uid_logger("start register by phone: type is #{type}")
else pre = 'p'
tip_exception(-1, interactor.error) email = nil
end phone = params[:login]
rescue Register::BaseForm::EmailError => e # verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last
render_result(-2, e.message) # TODO: 暂时限定邮箱注册
rescue Register::BaseForm::LoginError => e return normal_status(-1, '只支持邮箱注册')
render_result(-3, e.message) else
rescue Register::BaseForm::PhoneError => e uid_logger("start register by email: type is #{type}")
render_result(-4, e.message) pre = 'm'
rescue Register::BaseForm::PasswordFormatError => e email = params[:login]
render_result(-5, e.message) phone = nil
rescue Register::BaseForm::PasswordConfirmationError => e return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login])
render_result(-7, e.message) return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL
rescue Register::BaseForm::VerifiCodeError => e # verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last
render_result(-6, e.message) end
rescue Exception => e # uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}")
Gitea::User::DeleteService.call(user.login) unless user.nil? # check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60)
uid_logger_error(e.message) # todo 上线前请删除万能验证码"513231"
tip_exception(-1, e.message) return normal_status(-1, "8~16位密码支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD
end
end code = generate_identifier User, 8, pre
login = pre + code
# 用户登录 @user = User.new(admin: false, login: login, mail: email, phone: phone, type: "User")
def login @user.password = params[:password]
Users::LoginForm.new(login_params).validate! # 现在因为是验证码,所以在注册的时候就可以激活
@user = User.try_to_login(params[:login], params[:password]) @user.activate
# 必须要用save操作密码的保存是在users中
return normal_status(-2, "错误的账号或密码") if @user.blank?
# user is already in local database interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]})
return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? if interactor.success?
gitea_user = interactor.result
login_control = LimitForbidControl::UserLogin.new(@user) result = Gitea::User::GenerateTokenService.new(login, params[:password]).call
return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid? @user.gitea_token = result['sha1']
@user.gitea_uid = gitea_user[:body]['id']
password_ok = @user.check_password?(params[:password].to_s) if @user.save!
unless password_ok UserExtension.create!(user_id: @user.id)
if login_control.remain_times-1 == 0 successful_authentication(@user)
normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") normal_status("注册成功")
else end
normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会") else
end tip_exception(-1, interactor.error)
login_control.increment! end
return rescue Exception => e
end uid_logger_error(e.message)
tip_exception(-1, e.message)
successful_authentication(@user) end
sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步 end
# session[:user_id] = @user.id # 用户登录
end def login
Users::LoginForm.new(account_params).validate!
def change_password @user = User.try_to_login(params[:login], params[:password])
@user = User.find_by(login: params[:login])
return render_error("未找到相关用户!") if @user.blank? return normal_status(-2, "错误的账号或密码") if @user.blank?
return render_error("旧密码不正确") unless @user.check_password?(params[:old_password]) # user is already in local database
return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked?
sync_params = {
password: params[:password].to_s, login_control = LimitForbidControl::UserLogin.new(@user)
email: @user.mail return normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码") if login_control.forbid?
}
password_ok = @user.check_password?(params[:password].to_s)
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params) unless password_ok
if interactor.success? if login_control.remain_times-1 == 0
@user.update_attribute(:password, params[:password]) normal_status(-2, "登录密码出错已达上限,账号已被锁定, 请#{login_control.forbid_expires/60}分钟后重新登录或找回密码")
render_ok else
else normal_status(-2, "你已经输错密码#{login_control.error_times+1}次,还剩余#{login_control.remain_times-1}次机会")
render_error(interactor.error) end
end login_control.increment!
end return
end
# 忘记密码
def reset_password successful_authentication(@user)
begin sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) # TODO用户密码未同步
Accounts::ResetPasswordForm.new(reset_password_params).validate!
# session[:user_id] = @user.id
user = find_user end
return render_error('未找到相关账号') if user.blank?
def change_password
user = Accounts::ResetPasswordService.call(user, reset_password_params) @user = User.find_by(login: params[:login])
LimitForbidControl::UserLogin.new(user).clear if user.save! return render_error("未找到相关用户!") if @user.blank?
return render_error("旧密码不正确") unless @user.check_password?(params[:old_password])
render_ok
rescue Register::BaseForm::EmailError => e sync_params = {
render_result(-2, e.message) password: params[:password].to_s,
rescue Register::BaseForm::PhoneError => e email: @user.mail
render_result(-4, e.message) }
rescue Register::BaseForm::PasswordFormatError => e
render_result(-5, e.message) interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
rescue Register::BaseForm::PasswordConfirmationError => e if interactor.success?
render_result(-7, e.message) @user.update_attribute(:password, params[:password])
rescue Register::BaseForm::VerifiCodeError => e render_ok
render_result(-6, e.message) else
rescue ActiveRecord::Rollback => e render_error(interactor.error)
render_result(-1, "服务器异常") end
rescue Exception => e end
uid_logger_error(e.message)
tip_exception(e.message) # 忘记密码
end def reset_password
end begin
code = params[:code]
def successful_authentication(user) login_type = phone_mail_type(params[:login].strip)
uid_logger("Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}") # 获取验证码
# Valid user if login_type == 1
self.logged_user = user phone = params[:login]
# generate a key and set cookie if autologin verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 2).last
user = User.find_by_phone(phone)
set_autologin_cookie(user) else
UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip) email = params[:login]
user.update_column(:last_login_on, Time.now) verifi_code = VerificationCode.where(email: email, code: code, code_type: 3).last
session[:"#{default_yun_session}"] = user.id user = User.find_by_mail(email) #这里有问题应该是为email,而不是mail 6.13-hs
Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}") end
# 注册完成后有一天的试用申请(先去掉) return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip
# UserDayCertification.create(user_id: user.id, status: 1) return normal_status(-2, "验证码已失效") if !verifi_code&.effective?
end return normal_status(-1, "8~16位密码支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD
def set_autologin_cookie(user) user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation]
token = Token.get_or_create_permanent_login_token(user, "autologin") ActiveRecord::Base.transaction do
# sync_user_token_to_trustie(user.login, token.value) user.save!
LimitForbidControl::UserLogin.new(user).clear
cookie_options = { end
:value => token.value, sucess_status
:expires => 1.month.from_now, rescue Exception => e
:path => '/', uid_logger_error(e.message)
:secure => false, tip_exception(e.message)
:httponly => true end
} end
if edu_setting('cookie_domain').present?
cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain')) def successful_authentication(user)
end uid_logger("Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}")
cookies[autologin_cookie_name] = cookie_options # Valid user
cookies.signed[:user_id] ||= user.id self.logged_user = user
# generate a key and set cookie if autologin
logger.info("cookies is #{cookies} ======> #{cookies.signed[:user_id]} =====> #{cookies[autologin_cookie_name]}")
end set_autologin_cookie(user)
UserAction.create(:action_id => user.try(:id), :action_type => "Login", :user_id => user.try(:id), :ip => request.remote_ip)
def logout user.update_column(:last_login_on, Time.now)
Rails.logger.info("########___logout_current_user____________########{current_user.try(:id)}") session[:"#{default_yun_session}"] = user.id
UserAction.create(action_id: User.current.id, action_type: "Logout", user_id: User.current.id, :ip => request.remote_ip) Rails.logger.info("#########_____session_default_yun_session__________###############{default_yun_session}")
logout_user # 注册完成后有一天的试用申请(先去掉)
render :json => {status: 1, message: "退出成功!"} # UserDayCertification.create(user_id: user.id, status: 1)
end end
# 检验邮箱是否已被注册及邮箱或者手机号是否合法 def set_autologin_cookie(user)
# 参数type为事件类型 1注册2忘记密码3绑定 token = Token.get_or_create_permanent_login_token(user, "autologin")
def valid_email_and_phone sync_user_token_to_trustie(user.login, token.value)
check_mail_and_phone_valid(params[:login], params[:type])
end cookie_options = {
:value => token.value,
# 发送验证码 :expires => 1.month.from_now,
# params[:login] 手机号或者邮箱号 :path => '/',
# params[:type]为事件通知类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 :secure => false,
# 发送验证码send_type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱 :httponly => true
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9: 验收手机号有效 }
def get_verification_code if edu_setting('cookie_domain').present?
code = %W(0 1 2 3 4 5 6 7 8 9) cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
value = params[:login] end
type = params[:type].strip.to_i cookies[autologin_cookie_name] = cookie_options
login_type = phone_mail_type(value) cookies.signed[:user_id] ||= user.id
send_type = verify_type(login_type, type)
verification_code = code.sample(6).join logger.info("cookies is #{cookies} ======> #{cookies.signed[:user_id]} =====> #{cookies[autologin_cookie_name]}")
end
sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}")
tip_exception(501, "请求不合理") if sign != params[:smscode] def logout
Rails.logger.info("########___logout_current_user____________########{current_user.try(:id)}")
logger.info "########### 验证码:#{verification_code}" UserAction.create(action_id: User.current.id, action_type: "Logout", user_id: User.current.id, :ip => request.remote_ip)
logger.info("########get_verification_code: login_type #{login_type} send_type#{send_type}, ") logout_user
render :json => {status: 1, message: "退出成功!"}
# 记录验证码 end
check_verification_code(verification_code, send_type, value)
render_ok # 检验邮箱是否已被注册及邮箱或者手机号是否合法
end # 参数type为事件类型 1注册2忘记密码3绑定
def valid_email_and_phone
# check user's login or email or phone is used check_mail_and_phone_valid(params[:login], params[:type])
# params[:value] 手机号或者邮箱号或者登录名 end
# params[:type] 为事件类型 1登录名(login) 2email(邮箱) 3phone(手机号)
def check # 发送验证码
Register::CheckColumnsForm.new(check_params).validate! # params[:login] 手机号或者邮箱号
render_ok # params[:type]为事件通知类型 1用户注册注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
end # 发送验证码send_type 1注册手机验证码 2找回密码手机验证码 3找回密码邮箱验证码 4绑定手机 5绑定邮箱
# 6手机验证码登录 7邮箱验证码登录 8邮箱注册验证码 9: 验收手机号有效
private def get_verification_code
code = %W(0 1 2 3 4 5 6 7 8 9)
# type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 value = params[:login]
# login_type 1手机类型 2邮箱类型 type = params[:type].strip.to_i
def verify_type login_type, type login_type = phone_mail_type(value)
case type send_type = verify_type(login_type, type)
when 1 verification_code = code.sample(6).join
login_type == 1 ? 1 : 8
when 2 sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}")
login_type == 1 ? 2 : 3 tip_exception(501, "请求不合理") if sign != params[:smscode]
when 3
login_type == 1 ? 4 : tip_exception('请填写正确的手机号') logger.info("########get_verification_code: login_type #{login_type} send_type#{send_type}, ")
when 4
login_type == 1 ? tip_exception('请填写正确的邮箱') : 5 # 记录验证码
when 5 check_verification_code(verification_code, send_type, value)
login_type == 1 ? 9 : tip_exception('请填写正确的手机号') sucess_status
end end
end
# 1 手机类型0 邮箱类型
def generate_login(login) # 注意新版的login是自动名生成的
type = phone_mail_type(login.strip) def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
if type == 1 end
uid_logger("start register by phone: type is #{type}")
pre = 'p' private
email = nil
phone = login # type 事件类型 1用户注册 2忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
else # login_type 1手机类型 2邮箱类型
uid_logger("start register by email: type is #{type}") def verify_type login_type, type
pre = 'm' case type
email = login when 1
phone = nil login_type == 1 ? 1 : 8
end when 2
code = generate_identifier User, 8, pre login_type == 1 ? 2 : 3
when 3
{ login: pre + code, email: email, phone: phone } login_type == 1 ? 4 : tip_exception('请填写正确的手机号')
end when 4
login_type == 1 ? tip_exception('请填写正确的邮箱') : 5
def user_params when 5
params.require(:user).permit(:login, :email, :phone) login_type == 1 ? 9 : tip_exception('请填写正确的手机号')
end end
end
def login_params
params.require(:account).permit(:login, :password) def generate_login(login)
end type = phone_mail_type(login.strip)
def check_params if type == 1
params.permit(:type, :value) uid_logger("start register by phone: type is #{type}")
end pre = 'p'
email = nil
def register_params phone = login
params.permit(:login, :namespace, :password, :password_confirmation, :code) else
end uid_logger("start register by email: type is #{type}")
pre = 'm'
def reset_password_params email = login
params.permit(:login, :password, :password_confirmation, :code) phone = nil
end end
code = generate_identifier User, 8, pre
def find_user
phone_or_mail = strip(reset_password_params[:login]) { login: pre + code, email: email, phone: phone }
User.where("phone = :search OR mail = :search", search: phone_or_mail).last end
end
def sync_gitea_params
def remote_register_params params.permit(:login, :email, :password)
params.permit(:username, :email, :password, :platform) end
end
def user_params
end params.require(:user).permit(:login, :email, :phone)
end
def account_params
params.require(:account).permit(:login, :password)
end
end

View File

@ -22,7 +22,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController
max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?) max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?)
max_position = max_position_items.present? ? max_position_items.max.to_i : 0 max_position = max_position_items.present? ? max_position_items.max.to_i : 0
@project_category = ProjectCategory.new(name: @name,position: max_position, pinned_index: params[:project_category][:pinned_index].to_i) @project_category = ProjectCategory.new(name: @name,position: max_position)
if @project_category.save if @project_category.save
redirect_to admins_project_categories_path redirect_to admins_project_categories_path
flash[:success] = '创建成功' flash[:success] = '创建成功'
@ -33,18 +33,17 @@ class Admins::ProjectCategoriesController < Admins::BaseController
end end
def update def update
if @project_category.update_attributes({name: @name, pinned_index: params[:project_category][:pinned_index].to_i}) if @project_category.update_attribute(:name, @name)
save_image_file(params[:logo], 'logo')
redirect_to admins_project_categories_path redirect_to admins_project_categories_path
flash[:success] = '更新成功' flash[:success] = '更新成功'
else else
redirect_to admins_project_categories_path redirect_to admins_project_categories_path
flash[:danger] = '更新失败' flash[:success] = '更新失败'
end end
end end
def destroy def destroy
if @project_category.destroy if @project_language.destroy
redirect_to admins_project_categories_path redirect_to admins_project_categories_path
flash[:success] = "删除成功" flash[:success] = "删除成功"
else else
@ -81,12 +80,4 @@ class Admins::ProjectCategoriesController < Admins::BaseController
flash[:danger] = '分类已存在' flash[:danger] = '分类已存在'
end end
end end
def save_image_file(file, type)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
file_path = Util::FileManage.source_disk_filename(@project_category, type)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(file, file_path)
end
end end

View File

@ -1,5 +1,4 @@
class Admins::ProjectsController < Admins::BaseController class Admins::ProjectsController < Admins::BaseController
before_action :find_project, only: [:edit, :update]
def index def index
sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on'
@ -9,26 +8,6 @@ class Admins::ProjectsController < Admins::BaseController
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score) @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
end end
def edit ;end
def update
respond_to do |format|
if @project.update_attributes(project_update_params)
format.html do
redirect_to admins_projects_path
flash[:sucess] = "更新成功"
end
format.js {render_ok}
else
format.html do
redirect_to admins_projects_path
flash[:danger] = "更新失败"
end
format.js {render_js_error}
end
end
end
def destroy def destroy
project = Project.find_by!(id: params[:id]) project = Project.find_by!(id: params[:id])
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@ -42,13 +21,4 @@ class Admins::ProjectsController < Admins::BaseController
redirect_to admins_projects_path redirect_to admins_projects_path
flash[:danger] = "删除失败" flash[:danger] = "删除失败"
end end
private
def find_project
@project = Project.find_by_id(params[:id])
end
def project_update_params
params.require(:project).permit(:is_pinned, :recommend, :recommend_index)
end
end end

View File

@ -10,10 +10,6 @@ class Admins::SystemNotificationsController < Admins::BaseController
@notifications = paginate(notifications) @notifications = paginate(notifications)
end end
def history
@users = @notification.users
end
def new def new
@notification = SystemNotification.new @notification = SystemNotification.new
end end

View File

@ -1,57 +0,0 @@
class Admins::Topic::ActivityForumsController < Admins::Topic::BaseController
before_action :find_activity_forum, only: [:edit, :update, :destroy]
def index
q = ::Topic::ActivityForum.ransack(title_cont: params[:search])
activity_forums = q.result(distinct: true)
@activity_forums = paginate(activity_forums)
end
def new
@activity_forum = ::Topic::ActivityForum.new
end
def create
@activity_forum = ::Topic::ActivityForum.new(activity_forum_params)
if @activity_forum.save
redirect_to admins_topic_activity_forums_path
flash[:success] = "新增平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "新增平台动态失败"
end
end
def edit
end
def update
@activity_forum.attributes = activity_forum_params
if @activity_forum.save
redirect_to admins_topic_activity_forums_path
flash[:success] = "更新平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "更新平台动态失败"
end
end
def destroy
if @activity_forum.destroy
redirect_to admins_topic_activity_forums_path
flash[:success] = "删除平台动态成功"
else
redirect_to admins_topic_activity_forums_path
flash[:danger] = "删除平台动态失败"
end
end
private
def find_activity_forum
@activity_forum = ::Topic::ActivityForum.find_by_id(params[:id])
end
def activity_forum_params
params.require(:topic_activity_forum).permit(:title, :uuid, :url, :order_index)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::BannersController < Admins::Topic::BaseController
before_action :find_banner, only: [:edit, :update, :destroy]
def index
@banners = paginate(::Topic::Banner)
end
def new
@banner = ::Topic::Banner.new
end
def create
@banner = ::Topic::Banner.new(banner_params)
if @banner.save
save_image_file(params[:image], @banner)
redirect_to admins_topic_banners_path
flash[:success] = "新增banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "新增banner失败"
end
end
def edit
end
def update
@banner.attributes = banner_params
if @banner.save
save_image_file(params[:image], @banner)
redirect_to admins_topic_banners_path
flash[:success] = "更新banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "更新banner失败"
end
end
def destroy
if @banner.destroy
redirect_to admins_topic_banners_path
flash[:success] = "删除banner成功"
else
redirect_to admins_topic_banners_path
flash[:danger] = "删除banner失败"
end
end
private
def find_banner
@banner = ::Topic::Banner.find_by_id(params[:id])
end
def banner_params
params.require(:topic_banner).permit(:title, :order_index)
end
end

View File

@ -1,11 +0,0 @@
class Admins::Topic::BaseController < Admins::BaseController
protected
def save_image_file(file, topic)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
file_path = Util::FileManage.source_disk_filename(topic, 'image')
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(file, file_path)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::CardsController < Admins::Topic::BaseController
before_action :find_card, only: [:edit, :update, :destroy]
def index
q = ::Topic::Card.ransack(title_cont: params[:search])
cards = q.result(distinct: true)
@cards = paginate(cards)
end
def new
@card = ::Topic::Card.new
end
def create
@card = ::Topic::Card.new(card_params)
if @card.save
redirect_to admins_topic_cards_path
flash[:success] = "新增合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "新增合作单位失败"
end
end
def edit
end
def update
@card.attributes = card_params
if @card.save
redirect_to admins_topic_cards_path
flash[:success] = "更新合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "更新合作单位失败"
end
end
def destroy
if @card.destroy
redirect_to admins_topic_cards_path
flash[:success] = "删除合作单位成功"
else
redirect_to admins_topic_cards_path
flash[:danger] = "删除合作单位失败"
end
end
private
def find_card
@card = ::Topic::Card.find_by_id(params[:id])
end
def card_params
params.require(:topic_card).permit(:title, :url, :order_index)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::CooperatorsController < Admins::Topic::BaseController
before_action :find_cooperator, only: [:edit, :update, :destroy]
def index
@cooperators = paginate(::Topic::Cooperator)
end
def new
@cooperator = ::Topic::Cooperator.new
end
def create
@cooperator = ::Topic::Cooperator.new(cooperator_params)
if @cooperator.save
save_image_file(params[:image], @cooperator)
redirect_to admins_topic_cooperators_path
flash[:success] = "新增合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "新增合作单位失败"
end
end
def edit
end
def update
@cooperator.attributes = cooperator_params
if @cooperator.save
save_image_file(params[:image], @cooperator)
redirect_to admins_topic_cooperators_path
flash[:success] = "更新合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "更新合作单位失败"
end
end
def destroy
if @cooperator.destroy
redirect_to admins_topic_cooperators_path
flash[:success] = "删除合作单位成功"
else
redirect_to admins_topic_cooperators_path
flash[:danger] = "删除合作单位失败"
end
end
private
def find_cooperator
@cooperator = ::Topic::Cooperator.find_by_id(params[:id])
end
def cooperator_params
params.require(:topic_cooperator).permit(:title, :url, :order_index)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::ExcellentProjectsController < Admins::Topic::BaseController
before_action :find_excellent_project, only: [:edit, :update, :destroy]
def index
q = ::Topic::ExcellentProject.ransack(title_cont: params[:search])
excellent_projects = q.result(distinct: true)
@excellent_projects = paginate(excellent_projects)
end
def new
@excellent_project = ::Topic::ExcellentProject.new
end
def create
@excellent_project = ::Topic::ExcellentProject.new(excellent_project_params)
if @excellent_project.save
redirect_to admins_topic_excellent_projects_path
flash[:success] = "新增优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "新增优秀仓库失败"
end
end
def edit
end
def update
@excellent_project.attributes = excellent_project_params
if @excellent_project.save
redirect_to admins_topic_excellent_projects_path
flash[:success] = "更新优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "更新优秀仓库失败"
end
end
def destroy
if @excellent_project.destroy
redirect_to admins_topic_excellent_projects_path
flash[:success] = "删除优秀仓库成功"
else
redirect_to admins_topic_excellent_projects_path
flash[:danger] = "删除优秀仓库失败"
end
end
private
def find_excellent_project
@excellent_project = ::Topic::ExcellentProject.find_by_id(params[:id])
end
def excellent_project_params
params.require(:topic_excellent_project).permit(:title, :uuid, :url, :order_index)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::ExperienceForumsController < Admins::Topic::BaseController
before_action :find_experience_forum, only: [:edit, :update, :destroy]
def index
q = ::Topic::ExperienceForum.ransack(title_cont: params[:search])
experience_forums = q.result(distinct: true)
@experience_forums = paginate(experience_forums)
end
def new
@experience_forum = ::Topic::ExperienceForum.new
end
def create
@experience_forum = ::Topic::ExperienceForum.new(experience_forum_params)
if @experience_forum.save
redirect_to admins_topic_experience_forums_path
flash[:success] = "新增经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "新增经验分享失败"
end
end
def edit
end
def update
@experience_forum.attributes = experience_forum_params
if @experience_forum.save
redirect_to admins_topic_experience_forums_path
flash[:success] = "更新经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "更新经验分享失败"
end
end
def destroy
if @experience_forum.destroy
redirect_to admins_topic_experience_forums_path
flash[:success] = "删除经验分享成功"
else
redirect_to admins_topic_experience_forums_path
flash[:danger] = "删除经验分享失败"
end
end
private
def find_experience_forum
@experience_forum = ::Topic::ExperienceForum.find_by_id(params[:id])
end
def experience_forum_params
params.require(:topic_experience_forum).permit(:title, :uuid, :url, :order_index)
end
end

View File

@ -1,57 +0,0 @@
class Admins::Topic::PinnedForumsController < Admins::Topic::BaseController
before_action :find_pinned_forum, only: [:edit, :update, :destroy]
def index
q = ::Topic::PinnedForum.ransack(title_cont: params[:search])
pinned_forums = q.result(distinct: true)
@pinned_forums = paginate(pinned_forums)
end
def new
@pinned_forum = ::Topic::PinnedForum.new
end
def create
@pinned_forum = ::Topic::PinnedForum.new(pinned_forum_params)
if @pinned_forum.save
redirect_to admins_topic_pinned_forums_path
flash[:success] = "新增精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "新增精选文章失败"
end
end
def edit
end
def update
@pinned_forum.attributes = pinned_forum_params
if @pinned_forum.save
redirect_to admins_topic_pinned_forums_path
flash[:success] = "更新精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "更新精选文章失败"
end
end
def destroy
if @pinned_forum.destroy
redirect_to admins_topic_pinned_forums_path
flash[:success] = "删除精选文章成功"
else
redirect_to admins_topic_pinned_forums_path
flash[:danger] = "删除精选文章失败"
end
end
private
def find_pinned_forum
@pinned_forum = ::Topic::PinnedForum.find_by_id(params[:id])
end
def pinned_forum_params
params.require(:topic_pinned_forum).permit(:title, :uuid, :url, :order_index)
end
end

View File

@ -1,6 +1,4 @@
class Admins::UsersController < Admins::BaseController class Admins::UsersController < Admins::BaseController
before_action :finder_user, except: [:index]
def index def index
params[:sort_by] = params[:sort_by].presence || 'created_on' params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc' params[:sort_direction] = params[:sort_direction].presence || 'desc'
@ -10,9 +8,12 @@ class Admins::UsersController < Admins::BaseController
end end
def edit def edit
@user = User.find(params[:id])
end end
def update def update
@user = User.find(params[:id])
Admins::UpdateUserService.call(@user, update_params) Admins::UpdateUserService.call(@user, update_params)
flash[:success] = '保存成功' flash[:success] = '保存成功'
redirect_to edit_admins_user_path(@user) redirect_to edit_admins_user_path(@user)
@ -25,47 +26,43 @@ class Admins::UsersController < Admins::BaseController
end end
def destroy def destroy
@user.destroy! User.find(params[:id]).destroy!
Gitea::User::DeleteService.call(@user.login)
render_delete_success render_delete_success
end end
def lock def lock
@user.lock! User.find(params[:id]).lock!
render_ok render_ok
end end
def unlock def unlock
@user.activate! User.find(params[:id]).activate!
render_ok render_ok
end end
def reward_grade def reward_grade
user = User.find(params[:user_id])
return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0 return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0
RewardGradeService.call(@user, container_id: @user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true) RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true)
render_ok(grade: @user.grade) render_ok(grade: user.grade)
end end
def reset_login_times def reset_login_times
@user.reset_login_times! User.find(params[:id]).reset_login_times!
render_ok render_ok
end end
private private
def finder_user
@user = User.find(params[:id])
end
def update_params def update_params
params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker
mail phone location location_city school_id department_id admin business is_test mail phone location location_city school_id department_id admin business is_test
password professional_certification authentication login]) password professional_certification authentication])
end end
end end

View File

@ -26,8 +26,7 @@ class ApplicationController < ActionController::Base
end end
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 = "79e33abd4b6588941ab7622aed1e67e8"
helper_method :current_user, :base_url helper_method :current_user, :base_url
@ -71,11 +70,49 @@ class ApplicationController < ActionController::Base
(current_user.professional_certification && (ue.teacher? || ue.professional?)) (current_user.professional_certification && (ue.teacher? || ue.professional?))
end end
def shixun_marker
unless current_user.is_shixun_marker? || current_user.admin_or_business?
tip_exception(403, "..")
end
end
# 实训的访问权限
def shixun_access_allowed
if !current_user.shixun_permission(@shixun)
tip_exception(403, "..")
end
end
def admin_or_business? def admin_or_business?
User.current.admin? || User.current.business? User.current.admin? || User.current.business?
end end
# 访问课堂时没权限直接弹加入课堂的弹框 409
def user_course_identity
@user_course_identity = current_user.course_identity(@course)
if @user_course_identity > Course::STUDENT && @course.is_public == 0
tip_exception(401, "..") unless User.current.logged?
check_account
tip_exception(@course.excellent ? 410 : 409, "您没有权限进入")
end
if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id
# 实名认证和职业认证的身份判断
tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication &&
@course.professional_certification && (!current_user.authentication && !current_user.professional_certification)
tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication
tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification
end
uid_logger("###############user_course_identity:#{@user_course_identity}")
end
# 题库的访问权限
def bank_visit_auth
tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? ||
(current_user.certification_teacher? && @bank.is_public)
end
# 判断用户的邮箱或者手机是否可用 # 判断用户的邮箱或者手机是否可用
# 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
@ -83,16 +120,16 @@ class ApplicationController < ActionController::Base
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/ login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱") tip_exception(-2, "请输入正确的手机号或邮箱")
end end
# 考虑到安全参数问题多一次查询去掉Union
user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login) user = User.where(phone: login).first || User.where(mail: login).first
if user_exist && type.to_i == 1 if type.to_i == 1 && !user.nil?
tip_exception(-2, "该手机号码或邮箱已被注册") tip_exception(-2, "该手机号码或邮箱已被注册")
elsif type.to_i == 2 && !user_exist elsif type.to_i == 2 && user.nil?
tip_exception(-2, "该手机号码或邮箱未注册") tip_exception(-2, "该手机号码或邮箱未注册")
elsif type.to_i == 3 && user_exist elsif type.to_i == 3 && user.present?
tip_exception(-2, "该手机号码或邮箱已绑定") tip_exception(-2, "该手机号码或邮箱已绑定")
end end
render_ok sucess_status
end end
# 发送及记录激活码 # 发送及记录激活码
@ -103,7 +140,7 @@ 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 = Educoder::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0 tip_exception(-2, code_msg(status)) if status != 0
when 8, 3, 5 when 8, 3, 5
# 邮箱类型的发送 # 邮箱类型的发送
@ -149,6 +186,26 @@ class ApplicationController < ActionController::Base
end end
end end
def find_course
return normal_status(2, '缺少course_id参数') if params[:course_id].blank?
@course = Course.find(params[:course_id])
tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business?
rescue Exception => e
tip_exception(e.message)
end
def course_manager
return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR
end
def find_board
return normal_status(2, "缺少board_id参数") if params[:board_id].blank?
@board = Board.find(params[:board_id])
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
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
@ -158,6 +215,21 @@ class ApplicationController < ActionController::Base
@page_size = params[:page_size] || 15 @page_size = params[:page_size] || 15
end end
# 课堂教师权限
def teacher_allowed
logger.info("#####identity: #{current_user.course_identity(@course)}")
unless current_user.course_identity(@course) < Course::STUDENT
normal_status(403, "")
end
end
# 课堂教师、课堂管理员、超级管理员的权限(不包含助教)
def teacher_or_admin_allowed
unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR
normal_status(403, "")
end
end
def require_admin def require_admin
normal_status(403, "") unless User.current.admin? normal_status(403, "") unless User.current.admin?
end end
@ -184,7 +256,7 @@ class ApplicationController < ActionController::Base
# 异常提醒 # 异常提醒
def tip_exception(status = -1, message) def tip_exception(status = -1, message)
raise Gitlink::TipException.new(status, message) raise Educoder::TipException.new(status, message)
end end
def missing_template def missing_template
@ -193,7 +265,7 @@ class ApplicationController < ActionController::Base
# 弹框提醒 # 弹框提醒
def tip_show_exception(status = -2, message) def tip_show_exception(status = -2, message)
raise Gitlink::TipException.new(status, message) raise Educoder::TipException.new(status, message)
end end
def normal_status(status = 0, message) def normal_status(status = 0, message)
@ -265,6 +337,7 @@ class ApplicationController < ActionController::Base
User.current = user User.current = user
end end
end end
# if !User.current.logged? && Rails.env.development? # if !User.current.logged? && Rails.env.development?
# User.current = User.find 1 # User.current = User.find 1
# end # end
@ -336,6 +409,11 @@ class ApplicationController < ActionController::Base
@message = message @message = message
end end
# 实训等对应的仓库地址
def repo_ip_url(repo_path)
"#{edu_setting('git_address_ip')}/#{repo_path}"
end
def repo_url(repo_path) def repo_url(repo_path)
"#{edu_setting('git_address_domain')}/#{repo_path}" "#{edu_setting('git_address_domain')}/#{repo_path}"
end end
@ -368,7 +446,7 @@ class ApplicationController < ActionController::Base
JSON.parse(res) JSON.parse(res)
rescue Exception => e rescue Exception => e
uid_logger_error("--uri_exec: exception #{e.message}") uid_logger_error("--uri_exec: exception #{e.message}")
raise Gitlink::TipException.new("实训平台繁忙繁忙等级84") raise Educoder::TipException.new("实训平台繁忙繁忙等级84")
end end
end end
@ -387,7 +465,7 @@ class ApplicationController < ActionController::Base
end end
rescue Exception => e rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}") uid_logger("--uri_exec: exception #{e.message}")
raise Gitlink::TipException.new(message) raise Educoder::TipException.new(message)
end end
end end
@ -411,7 +489,7 @@ class ApplicationController < ActionController::Base
end end
rescue Exception => e rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}") uid_logger("--uri_exec: exception #{e.message}")
raise Gitlink::TipException.new("服务器繁忙") raise Educoder::TipException.new("服务器繁忙")
end end
end end
@ -583,8 +661,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 +682,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)
@ -612,7 +690,7 @@ class ApplicationController < ActionController::Base
def kaminari_array_paginate(relation) def kaminari_array_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
Kaminari.paginate_array(relation).page(page).per(limit) Kaminari.paginate_array(relation).page(page).per(limit)
@ -704,20 +782,14 @@ class ApplicationController < ActionController::Base
Rails.application.config_for(:configuration)['platform_url'] || request.base_url Rails.application.config_for(:configuration)['platform_url'] || request.base_url
end end
def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end
def convert_image! def convert_image!
@image = params[:image] @image = params[:image]
@image = @image.nil? && params[:user].present? ? params[:user][:image] : @image @image = @image.nil? && params[:user].present? ? params[:user][:image] : @image
return unless @image.present? return unless @image.present?
max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
if @image.class == ActionDispatch::Http::UploadedFile if @image.class == ActionDispatch::Http::UploadedFile
return render_error('请上传文件') if @image.size.zero? render_error('请上传文件') if @image.size.zero?
return render_error('文件大小超过限制') if @image.size > max_size.to_i render_error('文件大小超过限制') if @image.size > max_size.to_i
return render_error('头像格式不正确!') unless image_type?(File.extname(@image.original_filename.to_s)[1..-1])
else else
image = @image.to_s.strip image = @image.to_s.strip
return render_error('请上传正确的图片') if image.blank? return render_error('请上传正确的图片') if image.blank?
@ -743,10 +815,37 @@ class ApplicationController < ActionController::Base
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
@ -756,8 +855,4 @@ class ApplicationController < ActionController::Base
HotSearchKeyword.add(keyword) HotSearchKeyword.add(keyword)
end end
def find_atme_receivers
@atme_receivers = User.where(login: params[:receivers_login])
end
end end

View File

@ -196,7 +196,7 @@ class AttachmentsController < ApplicationController
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 = Educoder::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,

View File

@ -1,138 +0,0 @@
#coding=utf-8
class ClaimsController < ApplicationController
# skip_before_action :verify_authenticity_token
protect_from_forgery with: :null_session
before_action :require_login, except: [:index]
before_action :set_issue
def index
@user_claimed = 0
@claims = @issue.claims.claim_includes.order("created_at desc")
@claims.each do |claim|
if claim.user_id == current_user.id
@user_claimed = 1
break
end
end
render file: 'app/views/claims/list.json.jbuilder'
end
def create
@claim = Claim.find_by_sql(["select id from claims where issue_id=? and user_id=?",params[:issue_id], current_user.id])
if @claim.present?
return normal_status(-1,"您已经声明过该易修")
end
ActiveRecord::Base.transaction do
@claim = Claim.new(parse_issue_params(params))
if @claim.save
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 1
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "新建声明: #{params[:claim_note]}",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"新建声明操作失败")
end
end
end
def update
@claim = Claim.find_by_id(params[:claim_id])
if @claim.blank?
return normal_status(-1,"易修不存在")
end
if @claim.user_id != current_user.id
return normal_status(-1,"你不能更新别人的声明")
end
ActiveRecord::Base.transaction do
if @claim.update_attribute(:note,params[:claim_note])
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 1
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "更新声明: #{params[:claim_note]}",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"声明更新操作失败")
end
end
end
def destroy
@claim = Claim.find_by_sql(["select id from claims where issue_id=? and user_id=?",params[:issue_id], current_user.id])
if @claim.blank?
normal_status(-1,"您未曾声明过该易修")
else
@claim = @claim[0]
# 判断current user是否是claimer
ActiveRecord::Base.transaction do
if @claim.destroy
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 0
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "取消声明",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"取消声明操作失败")
end
end
end
end
private
def parse_issue_params(params)
{
issue_id: params[:issue_id],
user_id: current_user.id,
note: params[:claim_note],
}
end
def set_issue
@issue = Issue.find_by_id(params[:issue_id])
unless @issue.present?
normal_status(-1, "易修不存在")
end
end
end

View File

@ -42,14 +42,12 @@ class CompareController < ApplicationController
end end
def load_compare_params def load_compare_params
# @base = Addressable::URI.unescape(params[:base]) @base = Addressable::URI.unescape(params[:base])
@base = params[:base].include?(":") ? Addressable::URI.unescape(params[:base].split(":")[0]) + ':' + Base64.decode64(params[:base].split(":")[1]) : Base64.decode64(params[:base]) @head = params[:head].include?('json') ? params[:head]&.split('.json')[0] : params[:head]
@head = params[:head].include?('.json') ? params[:head][0..-6] : params[:head]
# @head = Addressable::URI.unescape(@head)
@head = @head.include?(":") ? Addressable::URI.unescape(@head.split(":")[0]) + ':' + Base64.decode64(@head.split(":")[1]) : Base64.decode64(@head)
end end
def gitea_compare(base, head) def gitea_compare(base, head)
Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, CGI.escape(base), CGI.escape(head), current_user.gitea_token) Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head, current_user.gitea_token)
end end
end end

View File

@ -20,7 +20,7 @@ module ControllerRescueHandler
end end
# rescue_from ActionView::MissingTemplate, with: :object_not_found # rescue_from ActionView::MissingTemplate, with: :object_not_found
# rescue_from ActiveRecord::RecordNotFound, with: :object_not_found # rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from Gitlink::TipException, with: :tip_show rescue_from Educoder::TipException, with: :tip_show
rescue_from ::ActionView::MissingTemplate, with: :missing_template rescue_from ::ActionView::MissingTemplate, with: :missing_template
rescue_from ActiveRecord::RecordNotFound, with: :object_not_found rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from ActionController::ParameterMissing, with: :render_parameter_missing rescue_from ActionController::ParameterMissing, with: :render_parameter_missing

View File

@ -36,10 +36,10 @@ module GitCommon
begin begin
@commits = GitService.commits(repo_path: @repo_path) @commits = GitService.commits(repo_path: @repo_path)
logger.info("git first commit is #{@commits.try(:first)}") logger.info("git first commit is #{@commits.try(:first)}")
raise Gitlink::TipException.new("请先创建版本库") if @commits.nil? raise Educoder::TipException.new("请先创建版本库") if @commits.nil?
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
raise Gitlink::TipException.new("提交记录异常") raise Educoder::TipException.new("提交记录异常")
end end
end end

View File

@ -34,7 +34,7 @@ module GitHelper
rescue Exception => e rescue Exception => e
Rails.logger.error(e.message) Rails.logger.error(e.message)
raise Gitlink::TipException.new("文档内容获取异常") raise Educoder::TipException.new("文档内容获取异常")
end end
end end
@ -64,7 +64,7 @@ module GitHelper
# 版本库Fork功能 # 版本库Fork功能
def project_fork(container, original_rep_path, username) def project_fork(container, original_rep_path, username)
raise Gitlink::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank? raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字 # 将要生成的仓库名字
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}" new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
# uid_logger("start fork container: repo_name is #{new_repo_name}") # uid_logger("start fork container: repo_name is #{new_repo_name}")

View File

@ -35,15 +35,15 @@ module LaboratoryHelper
# my_projects: "/users/#{current_user.try(:login)}/projects", # my_projects: "/users/#{current_user.try(:login)}/projects",
# my_projects: "https://www.trustie.net/users/#{current_user.try(:login)}/user_projectlist", # my_projects: "https://www.trustie.net/users/#{current_user.try(:login)}/user_projectlist",
{ {
new_syllabuses: "https://www.trustie.net/syllabuses/new", new_syllabuses: "https://forge.educoder.net/syllabuses/new",
new_course: "https://www.trustie.net/courses/new", new_course: "https://forge.educoder.net/courses/new",
edit_account: "https://www.trustie.net/my/account", edit_account: "https://forge.educoder.net/my/account",
my_courses: "https://www.trustie.net/users/#{current_user.try(:login)}/user_courselist", my_courses: "https://forge.educoder.net/users/#{current_user.try(:login)}/user_courselist",
my_projects: "/users/#{current_user.try(:login)}/projects", my_projects: "/users/#{current_user.try(:login)}/projects",
my_organ: "https://www.trustie.net/users/#{current_user.try(:login)}/user_organizations", my_organ: "https://forge.educoder.net/users/#{current_user.try(:login)}/user_organizations",
default_url: Rails.application.config_for(:configuration)['platform_url'], default_url: "https://www.educoder.net/",
tiding_url: "https://www.trustie.net/users/#{current_user.try(:login)}/user_messages", tiding_url: "https://www.educoder.net/users/#{current_user.try(:login)}/user_tidings",
register_url: "https://www.trustie.net/login?login=false" register_url: "https://www.educoder.net/user/register"
} }
end end
end end

View File

@ -1,61 +1,40 @@
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')
result = {message: nil, user: nil}
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
if need_edit_info user.activate
user.need_edit_info
else
user.activate
end
return unless user.valid? return unless user.valid?
create_gitea_user!(user, username, email, password)
end
def create_gitea_user!(forge_user, username, email, password=random_password)
result = {message: nil, user: nil}
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password}) interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
if interactor.success? if interactor.success?
gitea_user = interactor.result gitea_user = interactor.result
Rails.logger.info "##### [gitea] create_gitea_user result: #{gitea_user}"
result = Gitea::User::GenerateTokenService.call(username, password) result = Gitea::User::GenerateTokenService.call(username, password)
user.gitea_token = result['sha1'] forge_user.gitea_token = result['sha1']
user.gitea_uid = gitea_user[:body]['id'] forge_user.gitea_uid = gitea_user[:body]['id']
if user.save! forge_user.mail = email
UserExtension.create!(user_id: user.id) if forge_user.save
result[:user] = {id: user.id, token: user.gitea_token} UserExtension.create!(user_id: forge_user.id) if forge_user.user_extension.blank?
result[:user] = {id: forge_user.id, token: forge_user.gitea_token}
end end
else else
Rails.logger.info "##### [gitea] create_gitea_user result: #{interactor.error}"
result[:message] = interactor.error result[:message] = interactor.error
end end
result result
end end
def autosync_register_trustie(username, password, email, lastname="") def random_password
config = Rails.application.config_for(:configuration).symbolize_keys! "#{Random.rand(11111111)}"
api_host = config[:sync_url]
return if api_host.blank?
url = "#{api_host}/api/v1/users/common"
sync_json = {
"mail": email,
"password": password,
"login": username,
"lastname": lastname
}.compact
uri = URI.parse(url)
if api_host
http = Net::HTTP.new(uri.hostname, uri.port)
if api_host.include?("https://")
http.use_ssl = true
end
http.send_request('POST', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'})
end
end end
end end

View File

@ -28,8 +28,4 @@ module RenderHelper
def render_result(status=1, message='success') def render_result(status=1, message='success')
render json: { status: status, message: message } render json: { status: status, message: message }
end end
def render_parameter_missing
render json: { status: -1, message: '参数缺失' }
end
end end

View File

@ -6,6 +6,14 @@ class ForksController < ApplicationController
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
# TODO: fix Educoder shixun
if @new_project.persisted?
ProjectScore.create(:project_id => @new_project.id, :score => 0) if @new_project.project_score.nil?
project_info = ProjectInfo.new(:user_id => current_user.id, :project_id => @new_project.id)
@project.project_infos << project_info
end
end end
private private
@ -13,7 +21,7 @@ class ForksController < ApplicationController
if current_user&.id == @project.user_id if current_user&.id == @project.user_id
render_result(-1, "自己不能fork自己的项目") render_result(-1, "自己不能fork自己的项目")
elsif Project.exists?(user_id: current_user.id, identifier: @project.identifier) elsif Project.exists?(user_id: current_user.id, identifier: @project.identifier)
render_result(0, "fork失败你已拥有了这个项目") render_result(-1, "fork失败你已拥有了这个项目")
end end
# return if current_user != @project.owner # return if current_user != @project.owner
# render_result(-1, "自己不能fork自己的项目") # render_result(-1, "自己不能fork自己的项目")

View File

@ -2,7 +2,7 @@ class IssueTagsController < ApplicationController
before_action :require_login, except: [:index] before_action :require_login, except: [:index]
before_action :load_repository before_action :load_repository
before_action :set_user before_action :set_user
before_action :check_issue_tags_permission before_action :check_issue_permission, except: :index
before_action :set_issue_tag, only: [:edit, :update, :destroy] before_action :set_issue_tag, only: [:edit, :update, :destroy]
@ -17,7 +17,7 @@ class IssueTagsController < ApplicationController
def create def create
title = params[:name].to_s.strip.first(15) title = params[:name].to_s.strip.first(10)
desc = params[:description].to_s.first(30) desc = params[:description].to_s.first(30)
color = params[:color] || "#ccc" color = params[:color] || "#ccc"
@ -29,7 +29,7 @@ class IssueTagsController < ApplicationController
if title.present? if title.present?
if IssueTag.exists?(name: title, project_id: @project.id) if IssueTag.exists?(name: title, project_id: @project.id)
normal_status(-1, "项目标记已存在") normal_status(-1, "标签已存在")
else else
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
@ -37,12 +37,12 @@ class IssueTagsController < ApplicationController
if issue_tag.save if issue_tag.save
# gitea_tag = Gitea::Labels::CreateService.new(current_user, @repository.try(:identifier), tag_params).call # gitea_tag = Gitea::Labels::CreateService.new(current_user, @repository.try(:identifier), tag_params).call
# if gitea_tag && issue_tag.update_attributes(gid: gitea_tag["id"], gitea_url: gitea_tag["url"]) # if gitea_tag && issue_tag.update_attributes(gid: gitea_tag["id"], gitea_url: gitea_tag["url"])
normal_status(0, "项目标记创建成功!") # normal_status(0, "标签创建成功")
# else # else
# normal_status(-1, "项目标记创建失败") # normal_status(-1, "标签创建失败")
# end # end
else else
normal_status(-1, "项目标记创建失败") normal_status(-1, "标签创建失败")
end end
rescue => e rescue => e
puts "create version release error: #{e.message}" puts "create version release error: #{e.message}"
@ -51,7 +51,7 @@ class IssueTagsController < ApplicationController
end end
end end
else else
normal_status(-1, "项目标记名称不能为空") normal_status(-1, "标签名称不能为空")
end end
end end
@ -60,8 +60,8 @@ class IssueTagsController < ApplicationController
end end
def update def update
title = params[:name].to_s.strip.first(15) title = params[:name]
desc = params[:description].to_s.first(30) desc = params[:description]
color = params[:color] || "#ccc" color = params[:color] || "#ccc"
tag_params = { tag_params = {
@ -71,19 +71,19 @@ class IssueTagsController < ApplicationController
} }
if title.present? if title.present?
if IssueTag.exists?(name: title, project_id: @project.id) && (@issue_tag.name != title) if IssueTag.exists?(name: title, project_id: @project.id) && (@issue_tag.name != title)
normal_status(-1, "项目标记已存在") normal_status(-1, "标签已存在")
else else
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
if @issue_tag.update_attributes(tag_params) if @issue_tag.update_attributes(tag_params)
# gitea_tag = Gitea::Labels::UpdateService.new(current_user, @repository.try(:identifier),@issue_tag.try(:gid), tag_params).call # gitea_tag = Gitea::Labels::UpdateService.new(current_user, @repository.try(:identifier),@issue_tag.try(:gid), tag_params).call
# if gitea_tag # if gitea_tag
# normal_status(0, "项目标记更新成功") # normal_status(0, "标签更新成功")
# else # else
# normal_status(-1, "项目标记更新失败") # normal_status(-1, "标签更新失败")
# end # end
else else
normal_status(-1, "项目标记更新失败") normal_status(-1, "标签更新失败")
end end
rescue => e rescue => e
puts "create version release error: #{e.message}" puts "create version release error: #{e.message}"
@ -92,7 +92,7 @@ class IssueTagsController < ApplicationController
end end
end end
else else
normal_status(-1, "项目标记名称不能为空") normal_status(-1, "标签名称不能为空")
end end
end end
@ -102,12 +102,12 @@ class IssueTagsController < ApplicationController
if @issue_tag.destroy if @issue_tag.destroy
# issue_tag = Gitea::Labels::DeleteService.new(@user, @repository.try(:identifier), @issue_tag.try(:gid)).call # issue_tag = Gitea::Labels::DeleteService.new(@user, @repository.try(:identifier), @issue_tag.try(:gid)).call
# if issue_tag # if issue_tag
# normal_status(0, "项目标记删除成功") # normal_status(0, "标签删除成功")
# else # else
# normal_status(-1, "项目标记删除失败") # normal_status(-1, "标签删除失败")
# end # end
else else
normal_status(-1, "项目标记删除失败") normal_status(-1, "标签删除失败")
end end
rescue => e rescue => e
puts "create version release error: #{e.message}" puts "create version release error: #{e.message}"
@ -122,16 +122,16 @@ class IssueTagsController < ApplicationController
@user = @project.owner @user = @project.owner
end end
def check_issue_tags_permission def check_issue_permission
unless @project.manager?(current_user) || current_user.admin? unless @project.member?(current_user) || current_user.admin?
return render_forbidden('你不是管理员,没有权限操作') normal_status(-1, "您没有权限")
end end
end end
def set_issue_tag def set_issue_tag
@issue_tag = IssueTag.find_by_id(params[:id]) @issue_tag = IssueTag.find_by_id(params[:id])
unless @issue_tag.present? unless @issue_tag.present?
normal_status(-1, "项目标记不存在") normal_status(-1, "标签不存在")
end end
end end

View File

@ -9,13 +9,12 @@ class IssuesController < ApplicationController
before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue] before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue]
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, only: [:create, :update]
include ApplicationHelper include ApplicationHelper
include TagChosenHelper include TagChosenHelper
def index def index
@user_operate_issue = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user))
@user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user) || @project.is_public?) @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user) || @project.is_public?)
issues = @project.issues.issue_issue.issue_index_includes issues = @project.issues.issue_issue.issue_index_includes
issues = issues.where(is_private: false) unless @user_admin_or_member issues = issues.where(is_private: false) unless @user_admin_or_member
@ -126,15 +125,9 @@ class IssuesController < ApplicationController
end end
end end
if params[:issue_tag_ids].present? if params[:issue_tag_ids].present?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1 params[:issue_tag_ids].each do |tag|
return normal_status(-1, "最多只能创建一个标记。") IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1 end
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 end
if params[:assigned_to_id].present? if params[:assigned_to_id].present?
Tiding.create!(user_id: params[:assigned_to_id], trigger_user_id: current_user.id, Tiding.create!(user_id: params[:assigned_to_id], trigger_user_id: current_user.id,
@ -149,11 +142,7 @@ class IssuesController < ApplicationController
end end
@issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
render json: {status: 0, message: "创建成", id: @issue.id}
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0
render json: {status: 0, message: "创建成功", id: @issue.id}
else else
normal_status(-1, "创建失败") normal_status(-1, "创建失败")
end end
@ -172,17 +161,11 @@ class IssuesController < ApplicationController
last_token = @issue.token last_token = @issue.token
last_status_id = @issue.status_id last_status_id = @issue.status_id
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
if params[:issue_tag_ids].present? if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1 @issue&.issue_tags_relates&.destroy_all
return normal_status(-1, "最多只能创建一个标记。") params[:issue_tag_ids].each do |tag|
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1 IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
@issue&.issue_tags_relates&.destroy_all end
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 end
issue_files = params[:attachment_ids] issue_files = params[:attachment_ids]
@ -261,10 +244,6 @@ class IssuesController < ApplicationController
post_to_chain(change_type, change_token.abs, current_user.try(:login)) post_to_chain(change_type, change_token.abs, current_user.try(:login))
end end
@issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present? @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present?
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0
normal_status(0, "更新成功") normal_status(0, "更新成功")
else else
normal_status(-1, "更新失败") normal_status(-1, "更新失败")
@ -486,8 +465,7 @@ class IssuesController < ApplicationController
end end
def operate_issue_permission def operate_issue_permission
@issue = Issue.find_by_id(params[:id]) unless @issue.present? return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || @project.is_public?)
return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || (@project.is_public && @issue.nil?) || (@project.is_public && @issue.present? && @issue.author_id == current_user.id))
end end
def export_issues(issues) def export_issues(issues)

View File

@ -1,6 +1,6 @@
class JournalsController < ApplicationController class JournalsController < ApplicationController
before_action :require_login, except: [:index, :get_children_journals] before_action :require_login, except: [:index, :get_children_journals]
before_action :require_profile_completed, :find_atme_receivers, only: [:create] before_action :require_profile_completed, only: [:create]
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]
@ -22,35 +22,32 @@ class JournalsController < ApplicationController
if notes.blank? if notes.blank?
normal_status(-1, "评论内容不能为空") normal_status(-1, "评论内容不能为空")
else else
ActiveRecord::Base.transaction do journal_params = {
journal_params = { journalized_id: @issue.id ,
journalized_id: @issue.id , journalized_type: "Issue",
journalized_type: "Issue", user_id: current_user.id ,
user_id: current_user.id , notes: notes.to_s.strip,
notes: notes.to_s.strip, parent_id: params[:parent_id]
parent_id: params[:parent_id] }
} journal = Journal.new journal_params
journal = Journal.new journal_params if journal.save
if journal.save if params[:attachment_ids].present?
if params[:attachment_ids].present? params[:attachment_ids].each do |id|
params[:attachment_ids].each do |id| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) unless attachment.blank?
unless attachment.blank? attachment.container = journal
attachment.container = journal attachment.author_id = current_user.id
attachment.author_id = current_user.id attachment.description = ""
attachment.description = "" attachment.save
attachment.save
end
end end
end end
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, journal) if @atme_receivers.size > 0
# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
render :json => { status: 0, message: "评论成功", id: journal.id}
# normal_status(0, "评论成功")
else
normal_status(-1, "评论失败")
end end
# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
render :json => { status: 0, message: "评论成功", id: journal.id}
# normal_status(0, "评论成功")
else
normal_status(-1, "评论失败")
end end
end end
end end

View File

@ -21,6 +21,14 @@ class MainController < ApplicationController
uid_logger("main start is #{cookies[:_educoder_session]}") uid_logger("main start is #{cookies[:_educoder_session]}")
end end
if params[:path] && params[:path].start_with?('projects/')
project_id = params[:path].split("/")[1]
project = Project.find_by_id(project_id)
if project.present?
return redirect_to("/projects/#{project.owner.login}/#{project.identifier}", status: 301)
end
end
# TODO: 这块之后需要整合者架构重新变化统一跳转到index后再路由分发 # TODO: 这块之后需要整合者架构重新变化统一跳转到index后再路由分发
if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild" if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild"
render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html' render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html'

View File

@ -3,11 +3,19 @@ class MembersController < ApplicationController
before_action :load_project before_action :load_project
before_action :find_user_with_id, only: %i[create remove change_role] before_action :find_user_with_id, only: %i[create remove change_role]
before_action :check_user_profile_completed, only: [:create] before_action :check_user_profile_completed, only: [:create]
before_action :operate! before_action :operate!, except: %i[index]
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]
def create def create
if @user.gitea_token.blank?
result = Gitea::User::GetService.call(@user.login)
if result[:status] == :error
# gitea不存在用户@user,直接注册
user_result = create_gitea_user!(@user, @user.login, @user.mail)
return render_error(user_result[:message]) if user_result[:message]
end
end
interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user) interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user)
SendTemplateMessageJob.perform_later('ProjectJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('ProjectJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('ProjectMemberJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('ProjectMemberJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu?

View File

@ -2,7 +2,6 @@ class Oauth::BaseController < ActionController::Base
include RenderHelper include RenderHelper
include LoginHelper include LoginHelper
include ControllerRescueHandler include ControllerRescueHandler
include LoggerHelper
# include LaboratoryHelper # include LaboratoryHelper
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
@ -12,18 +11,6 @@ class Oauth::BaseController < ActionController::Base
end end
private private
def tip_exception(status = -1, message)
raise Educoder::TipException.new(status, message)
end
def tip_show_exception(status = -2, message)
raise Educoder::TipException.new(status, message)
end
def tip_show(exception)
uid_logger("Tip show status is #{exception.status}, message is #{exception.message}")
render json: exception.tip_json
end
def session_user_id def session_user_id
# session[:user_id] # session[:user_id]
@ -61,13 +48,4 @@ class Oauth::BaseController < ActionController::Base
Rails.logger.info("[wechat] set session unionid: #{unionid}") Rails.logger.info("[wechat] set session unionid: #{unionid}")
session[:unionid] = unionid session[:unionid] = unionid
end end
def session_edulogin
session[:edulogin]
end
def set_session_edulogin(login)
Rails.logger.info("[educoder] set sesstion edulogin: #{login}")
session[:edulogin] = login
end
end end

View File

@ -1,6 +1,4 @@
class Oauth::EducoderController < Oauth::BaseController class Oauth::EducoderController < Oauth::BaseController
include RegisterHelper
def bind def bind
begin begin
login = params[:login] login = params[:login]
@ -34,41 +32,4 @@ class Oauth::EducoderController < Oauth::BaseController
render_error(ex.message) render_error(ex.message)
end end
end end
# 需要educoder那边设置回调地址
def create
begin
code = params['code'].to_s.strip
tip_exception("code不能为空") if code.blank?
new_user = false
result = EducoderOauth::Service.access_token(code)
result = EducoderOauth::Service.user_info(result[:access_token])
# 存在该用户
open_user = OpenUsers::Educoder.find_by(uid: result['login'])
if open_user.present? && open_user.user.present?
successful_authentication(open_user.user)
else
if current_user.blank? || !current_user.logged?
new_user = true
login = User.generate_login('E')
reg_result = autologin_register(login,"#{login}@forge.com", "Ec#{login}2021#", 'educoder', true)
if reg_result[:message].blank?
open_user = OpenUsers::Educoder.create!(user_id: reg_result[:user][:id], uid: result['login'], extra: result)
autosync_register_trustie(login, "Ec#{login}2021#", "#{login}@forge.com")
successful_authentication(open_user.user)
else
render_error(reg_result[:message])
end
else
OpenUsers::Educoder.create!(user: current_user, uid: result['login'], extra: result)
end
end
redirect_to root_path(new_user: new_user)
rescue Exception => ex
render_error(ex.message)
end
end
end end

View File

@ -61,7 +61,6 @@ class OauthController < ApplicationController
login = params[:login] login = params[:login]
email = params[:mail] email = params[:mail]
password = params[:password] password = params[:password]
lastname = params[:lastname]
callback_url = params[:callback_url] callback_url = params[:callback_url]
platform = params[:plathform] || 'educoder' platform = params[:plathform] || 'educoder'
@ -73,11 +72,8 @@ class OauthController < ApplicationController
if result[:message].blank? if result[:message].blank?
logger.info "[Oauth educoer] ====auto_register success" logger.info "[Oauth educoer] ====auto_register success"
user = User.find result[:user][:id] user = User.find result[:user][:id]
user.update_column(:lastname, params[:lastname])
autosync_register_trustie(login, password, email, lastname)
OpenUsers::Educoder.create!(user: user, uid: user.login)
successful_authentication(user) successful_authentication(user)
OpenUsers::Educoder.create!(user: user, uid: user.login)
render json: { callback_url: callback_url } render json: { callback_url: callback_url }
# redirect_to callback_url # redirect_to callback_url

View File

@ -22,13 +22,12 @@ class Organizations::OrganizationsController < Organizations::BaseController
@can_create_project = @organization.can_create_project?(current_user.id) @can_create_project = @organization.can_create_project?(current_user.id)
@is_admin = can_edit_org? @is_admin = can_edit_org?
@is_member = @organization.is_member?(current_user.id) @is_member = @organization.is_member?(current_user.id)
Cache::V2::OwnerCommonService.new(@organization.id).read
end end
def create def create
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name]) tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.is_reversed(organization_params[:name]).present?
Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate! Organizations::CreateForm.new(organization_params).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?
end end
@ -39,14 +38,14 @@ class Organizations::OrganizationsController < Organizations::BaseController
def update def update
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
Organizations::CreateForm.new(organization_params.merge(original_name: @organization.login)).validate! Organizations::CreateForm.new(organization_params).validate!
login = @organization.login login = @organization.login
@organization.login = organization_params[:name] if organization_params[:name].present? @organization.login = organization_params[:name] if organization_params[:name].present?
@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!
Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload) Gitea::Organization::UpdateService.call(@organization.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?
end end
rescue Exception => e rescue Exception => e
@ -57,16 +56,10 @@ class Organizations::OrganizationsController < Organizations::BaseController
def destroy def destroy
tip_exception("密码不正确") unless current_user.check_password?(password) tip_exception("密码不正确") unless current_user.check_password?(password)
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
gitea_destroy = Gitea::Organization::DeleteService.call(current_user.gitea_token, @organization.login) Gitea::Organization::DeleteService.call(@organization.gitea_token, @organization.login)
if gitea_destroy[:status] == 204 @organization.destroy!
@organization.destroy!
render_ok
elsif gitea_destroy[:status] == 500
tip_exception("当组织内含有仓库时,无法删除此组织")
else
tip_exception("")
end
end end
render_ok
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
tip_exception(e.message) tip_exception(e.message)
@ -75,7 +68,8 @@ class Organizations::OrganizationsController < Organizations::BaseController
def recommend def recommend
recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer) recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer)
@organizations = Organization.includes(:organization_extension).where(organization_extensions: {recommend: true}).to_a.each_slice(group_size).to_a @organizations = Organization.with_visibility(%w(common))
.where(login: recommend).select(:id, :login, :firstname, :lastname, :nickname)
end end
private private
@ -86,10 +80,6 @@ class Organizations::OrganizationsController < Organizations::BaseController
:max_repo_creation, :nickname) :max_repo_creation, :nickname)
end end
def group_size
params.fetch(:group_size, 4).to_i
end
def password def password
params.fetch(:password, "") params.fetch(:password, "")
end end

View File

@ -18,7 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id) @team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id)
@organization_user = OrganizationUser.build(@organization.id, @operate_user.id) @organization_user = OrganizationUser.build(@organization.id, @operate_user.id)
SendTemplateMessageJob.perform_later('TeamJoined', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('OrganizationRole', @operate_user.id, @organization.id, @team.authorize_name) if Site.has_notice_menu?
Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login) Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
end end
rescue Exception => e rescue Exception => e
@ -31,7 +31,6 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@team_user.destroy! @team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login) Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
SendTemplateMessageJob.perform_later('TeamLeft', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu?
org_team_users = @organization.team_users.where(user_id: @operate_user.id) org_team_users = @organization.team_users.where(user_id: @operate_user.id)
unless org_team_users.present? unless org_team_users.present?
@organization.organization_users.find_by(user_id: @operate_user.id).destroy! @organization.organization_users.find_by(user_id: @operate_user.id).destroy!

View File

@ -4,24 +4,15 @@ class Organizations::TeamsController < Organizations::BaseController
before_action :check_user_can_edit_org, only: [:create, :update, :destroy] before_action :check_user_can_edit_org, only: [:create, :update, :destroy]
def index def index
#if @organization.is_owner?(current_user) || current_user.admin?
if params[:is_full].present?
if can_edit_org?
@teams = @organization.teams
else
@teams = []
end
else
#if @organization.is_owner?(current_user) || current_user.admin?
@teams = @organization.teams @teams = @organization.teams
#else #else
# @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id}) # @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
#end #end
@is_admin = can_edit_org? @is_admin = can_edit_org?
@teams = @teams.includes(:team_units, :team_users) @teams = @teams.includes(:team_units, :team_users)
@teams = kaminari_paginate(@teams) @teams = kaminari_paginate(@teams)
end
end end
def search def search
@ -43,12 +34,8 @@ class Organizations::TeamsController < Organizations::BaseController
def create def create
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
if @organization.teams.count >= 50 Organizations::CreateTeamForm.new(team_params).validate!
return render_forbidden("组织的团队数量已超过限制!") @team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
else
Organizations::CreateTeamForm.new(team_params).validate!
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
end
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)

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
@ -20,7 +20,7 @@ class OwnersController < ApplicationController
@is_admin = current_user.admin? || @owner.is_owner?(current_user.id) @is_admin = current_user.admin? || @owner.is_owner?(current_user.id)
@is_member = @owner.is_member?(current_user.id) @is_member = @owner.is_member?(current_user.id)
# 用户 # 用户
elsif @owner.is_a?(User) else
#待办事项,现在未做 #待办事项,现在未做
if User.current.admin? || User.current.login == @owner.login if User.current.admin? || User.current.login == @owner.login
@waiting_applied_messages = @owner.applied_messages.waiting @waiting_applied_messages = @owner.applied_messages.waiting
@ -45,6 +45,7 @@ class OwnersController < ApplicationController
@projects_common_count = user_projects.common.size @projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size @projects_mirrior_count = user_projects.mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size @projects_sync_mirrior_count = user_projects.sync_mirror.size
puts @owner.as_json
end end
end end

View File

@ -5,10 +5,6 @@ class ProjectCategoriesController < ApplicationController
@project_categories = q.result(distinct: true) @project_categories = q.result(distinct: true)
end end
def pinned_index
@project_categories = ProjectCategory.where.not(pinned_index: 0).order(pinned_index: :desc)
end
def group_list def group_list
@project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc) @project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc)
# projects = Project.no_anomory_projects.visible # projects = Project.no_anomory_projects.visible

View File

@ -1,26 +0,0 @@
class ProjectRankController < ApplicationController
# 根据时间获取热门项目
def index
$redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names)
deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
$redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank?
@project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true)
rescue Exception => e
@project_rank = []
end
private
# 默认显示7天的
def time
params.fetch(:time, 7).to_i
end
def get_timeable_key_names
names_array = []
(0...time).to_a.each do |i|
date_time_string = (Date.today - i.days).to_s
names_array << "v2-project-rank-#{date_time_string}"
end
names_array
end
end

View File

@ -3,7 +3,7 @@ class ProjectTrendsController < ApplicationController
before_action :check_project_public before_action :check_project_public
def index def index
project_trends = @project.project_trends.preload(:user, trend: :user, project: :owner) project_trends = @project.project_trends.preload(:user, trend: :user)
check_time = params[:time] #时间的筛选 check_time = params[:time] #时间的筛选
check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version

View File

@ -1,6 +0,0 @@
class Projects::MembersController < Projects::BaseController
def index
users = @project.all_collaborators.like(params[:search]).includes(:user_extension)
@users = kaminari_paginate(users)
end
end

View File

@ -4,9 +4,9 @@ class ProjectsController < ApplicationController
include ProjectsHelper include ProjectsHelper
include Acceleratorable include Acceleratorable
before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list] before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend about menu_list]
before_action :require_profile_completed, only: [:create, :migrate] before_action :require_profile_completed, only: [:create, :migrate]
before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend] before_action :load_repository, except: %i[index group_type_list migrate create recommend]
before_action :authorizate_user_can_edit_project!, only: %i[update] before_action :authorizate_user_can_edit_project!, only: %i[update]
before_action :project_public?, only: %i[fork_users praise_users watch_users] before_action :project_public?, only: %i[fork_users praise_users watch_users]
@ -16,13 +16,13 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("home")) menu.append(menu_hash_by_name("home"))
menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code") menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code")
menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues") menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues")
menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls") && @project.forge? menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls")
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge? menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki")
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops")
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("resources")) if @project.has_menu_permission("resources")
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 (current_user.admin? || @project.manager?(current_user)) && @project.forge? menu.append(menu_hash_by_name("settings")) if current_user.admin? || @project.manager?(current_user)
render json: menu render json: menu
end end
@ -30,8 +30,8 @@ class ProjectsController < ApplicationController
def index def index
scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params) scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params)
@projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)) # @projects = kaminari_paginate(scope)
# @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)
category_id = params[:category_id] category_id = params[:category_id]
@total_count = @total_count =
@ -49,6 +49,13 @@ class ProjectsController < ApplicationController
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
# TODO: fix Educoder shixun
if @project.persisted?
ProjectScore.create(:project_id => @project.id, :score => 0) if @project.project_score.nil?
project_info = ProjectInfo.new(:user_id => current_user.id, :project_id => @project.id)
@project.project_infos << project_info
end
end end
rescue Exception => e rescue Exception => e
uid_logger_error(e.message) uid_logger_error(e.message)
@ -82,9 +89,8 @@ class ProjectsController < ApplicationController
def branches def branches
return @branches = [] unless @project.forge? return @branches = [] unless @project.forge?
# result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier) result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier)
result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier) @branches = result.is_a?(Hash) && result.key?(:status) ? [] : result
@branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result["branch_name"]) : result
end end
def branches_slice def branches_slice
@ -191,8 +197,6 @@ class ProjectsController < ApplicationController
end end
def simple def simple
# 为了缓存活跃项目的基本信息,后续删除
Cache::V2::ProjectCommonService.new(@project.id).read
json_response(@project, current_user) json_response(@project, current_user)
end end
@ -200,10 +204,6 @@ class ProjectsController < ApplicationController
@projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc) @projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc)
end end
def banner_recommend
@projects = Project.recommend.where.not(recommend_index: 0).includes(:project_category, :owner, :project_language).order(recommend_index: :desc)
end
def about def about
@project_detail = @project.project_detail @project_detail = @project.project_detail
@attachments = Array(@project_detail&.attachments) if request.get? @attachments = Array(@project_detail&.attachments) if request.get?

View File

@ -5,7 +5,6 @@ class PullRequestsController < ApplicationController
before_action :check_menu_authorize before_action :check_menu_authorize
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]
include TagChosenHelper include TagChosenHelper
include ApplicationHelper include ApplicationHelper
@ -56,22 +55,17 @@ class PullRequestsController < ApplicationController
end end
def create def create
# return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user)
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@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"])
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)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
else else
render_error("create pull request error: #{@gitea_pull_request[:status]}") render_error("create pull request error: #{@gitea_pull_request[:status]}")
raise ActiveRecord::Rollback raise ActiveRecord::Rollback
end end
end end
rescue => e
normal_status(-1, e.message)
end end
def edit def edit
@ -81,7 +75,6 @@ class PullRequestsController < ApplicationController
end end
def update def update
return render_forbidden("你没有权限操作.") unless @project.operator?(current_user)
if params[:title].nil? if params[:title].nil?
normal_status(-1, "名称不能为空") normal_status(-1, "名称不能为空")
elsif params[:issue_tag_ids].nil? elsif params[:issue_tag_ids].nil?
@ -89,21 +82,14 @@ class PullRequestsController < ApplicationController
else else
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
begin begin
return normal_status(-1, "title不能超过255个字符") if params[:title].length > 255
merge_params merge_params
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1 @issue&.issue_tags_relates&.destroy_all
return normal_status(-1, "最多只能创建一个标记。") params[:issue_tag_ids].each do |tag|
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1 IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
@issue&.issue_tags_relates&.destroy_all end
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 end
if @issue.update_attributes(@issue_params) if @issue.update_attributes(@issue_params)
@ -113,22 +99,13 @@ class PullRequestsController < ApplicationController
if gitea_pull[:status] === :success if gitea_pull[:status] === :success
if params[:issue_tag_ids].present? if params[:issue_tag_ids].present?
if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1 params[:issue_tag_ids].each do |tag|
return normal_status(-1, "最多只能创建一个标记。") IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1 end
@issue&.issue_tags_relates&.destroy_all
params[:issue_tag_ids].each do |tag|
IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
end
else
return normal_status(-1, "请输入正确的标记。")
end
end 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)
end end
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
normal_status(0, "PullRequest更新成功") normal_status(0, "PullRequest更新成功")
else else
normal_status(-1, "PullRequest更新失败") normal_status(-1, "PullRequest更新失败")
@ -215,7 +192,7 @@ class PullRequestsController < ApplicationController
def check_can_merge def check_can_merge
target_head = params[:head] #源分支 target_head = params[:head] #源分支
target_base = params[:base] #目标分支 target_base = params[:base] #目标分支
is_original = params[:is_original] || false is_original = params[:is_original]
if target_head.blank? || target_base.blank? if target_head.blank? || target_base.blank?
normal_status(-2, "请选择分支") normal_status(-2, "请选择分支")
elsif target_head === target_base && !is_original elsif target_head === target_base && !is_original
@ -246,11 +223,11 @@ class PullRequestsController < ApplicationController
private private
def load_pull_request 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]) @pull_request = PullRequest.find params[:id]
end end
def find_pull_request def find_pull_request
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id]) @pull_request = PullRequest.find_by_id(params[:id])
@issue = @pull_request&.issue @issue = @pull_request&.issue
if @pull_request.blank? if @pull_request.blank?
normal_status(-1, "合并请求不存在") normal_status(-1, "合并请求不存在")
@ -289,7 +266,7 @@ class PullRequestsController < ApplicationController
assigned_to_id: params[:assigned_to_id], assigned_to_id: params[:assigned_to_id],
fixed_version_id: params[:fixed_version_id], fixed_version_id: params[:fixed_version_id],
issue_tags_value: params[:issue_tag_ids].present? ? params[:issue_tag_ids].join(",") : "", issue_tags_value: params[:issue_tag_ids].present? ? params[:issue_tag_ids].join(",") : "",
priority_id: params[:priority_id], priority_id: params[:priority_id] || "2",
issue_classify: "pull_request", issue_classify: "pull_request",
issue_type: params[:issue_type] || "1", issue_type: params[:issue_type] || "1",
tracker_id: 2, tracker_id: 2,

View File

@ -48,7 +48,7 @@ class RepositoriesController < ApplicationController
def entries def entries
@project.increment!(:visits) @project.increment!(:visits)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
if @project.educoder? if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name) @entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else else
@ -64,6 +64,7 @@ class RepositoriesController < ApplicationController
def sub_entries def sub_entries
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
if @project.educoder? if @project.educoder?
if params[:type] === 'file' if params[:type] === 'file'
@ -71,35 +72,18 @@ class RepositoriesController < ApplicationController
logger.info "######### sub_entries: #{@sub_entries}" logger.info "######### sub_entries: #{@sub_entries}"
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1 return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = { tmp_entries = [{
"content" => @sub_entries['data']['content'], "content" => @sub_entries['data']['content'],
"type" => "blob" "type" => "blob"
} }]
@sub_entries = { @sub_entries = {
"trees"=>tmp_entries, "trees"=>tmp_entries,
"commits" => [{}] "commits" => [{}]
} }
else else
begin @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
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)
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
}
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
end
rescue
return render_error('该文件暂未开放,敬请期待.')
end
end end
else else
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref) interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success? if interactor.success?
result = interactor.result result = interactor.result
@ -111,17 +95,13 @@ class RepositoriesController < ApplicationController
end end
def commits def commits
if @project.educoder? if params[:filepath].present?
@commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name) file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else else
if params[:filepath].present? @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end
end end
end end
@ -132,12 +112,8 @@ class RepositoriesController < ApplicationController
def commit def commit
@sha = params[:sha] @sha = params[:sha]
if @project.educoder? @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
return render_error('暂未开放,敬请期待.') @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
end
end end
def tags def tags
@ -147,7 +123,7 @@ class RepositoriesController < ApplicationController
end end
def contributors def contributors
if params[:filepath].present? || @project.educoder? if params[:filepath].present?
@contributors = [] @contributors = []
else else
@contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
@ -219,26 +195,21 @@ class RepositoriesController < ApplicationController
else else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end end
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil @readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref])
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha")
rescue rescue
render json: nil render json: nil
end end
def languages def languages
if @project.educoder? render json: languages_precentagable
render json: {}
else
render json: languages_precentagable
end
end end
def archive def archive
domain = Gitea.gitea_config[:domain] domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url] api_url = Gitea.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{CGI.escape(params[:archive])}" archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{params[:archive]}"
file_path = [domain, api_url, archive_url].join file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden? file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
@ -252,11 +223,11 @@ class RepositoriesController < ApplicationController
domain = Gitea.gitea_config[:domain] domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url] api_url = Gitea.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{CGI.escape(params[:filepath])}?ref=#{CGI.escape(params[:ref])}" url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{params[:filepath]}?ref=#{params[:ref]}"
file_path = [domain, api_url, url].join file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") if @repository.hidden?
redirect_to file_path redirect_to URI.escape(file_path)
end end
private private
@ -376,7 +347,7 @@ class RepositoriesController < ApplicationController
local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id)) 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 if local_requests.save
gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call 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"]) if gitea_request[:status] == :success && local_requests.update_attributes(gitea_number: gitea_request["body"]["number"])
local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
end end
end end

View File

@ -4,7 +4,6 @@ class SettingsController < ApplicationController
get_add_menu get_add_menu
get_common_menu get_common_menu
get_personal_menu get_personal_menu
get_third_party
get_top_system_notification get_top_system_notification
end end
@ -41,14 +40,6 @@ class SettingsController < ApplicationController
end end
end end
def get_third_party
@third_party = []
@third_party << {
name: 'educoder',
url: EducoderOauth.oauth_url
}
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

@ -1,9 +0,0 @@
class TopicsController < ApplicationController
def index
return render_not_found("请输入正确的数据类型") unless params[:topic_type].present?
scope = Topic.with_single_type(params[:topic_type])
@topics = kaminari_paginate(scope)
end
end

View File

@ -1,24 +0,0 @@
class UserRankController < ApplicationController
# 根据时间获取热门开发者
def index
$redis_cache.zunionstore("recent-days-user-rank", get_timeable_key_names)
@user_rank = $redis_cache.zrevrange("recent-days-user-rank", 0, 3, withscores: true)
rescue Exception => e
@user_rank = []
end
private
# 默认显示7天的
def time
params.fetch(:time, 7).to_i
end
def get_timeable_key_names
names_array = []
(0...time).to_a.each do |i|
date_time_string = (Date.today - i.days).to_s
names_array << "v2-user-rank-#{date_time_string}"
end
names_array
end
end

View File

@ -188,32 +188,30 @@ class Users::StatisticsController < Users::BaseController
@project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count @project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count
@platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count @platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count
else else
@platform_result = Cache::V2::PlatformStatisticService.new.read
@user_result = Cache::V2::UserStatisticService.new(observed_user.id).read
# 用户被follow数量 # 用户被follow数量
@follow_count = @user_result["follow-count"].to_i @follow_count = Cache::UserFollowCountService.call(observed_user)
@platform_follow_count = @platform_result["follow-count"].to_i @platform_follow_count = Cache::PlatformFollowCountService.call
# 用户pr数量 # 用户pr数量
@pullrequest_count = @user_result["pullrequest-count"].to_i @pullrequest_count = Cache::UserPullrequestCountService.call(observed_user)
@platform_pullrequest_count = @platform_result["pullrequest-count"].to_i @platform_pullrequest_count = Cache::PlatformPullrequestCountService.call
# 用户issue数量 # 用户issue数量
@issues_count = @user_result["issue-count"].to_i @issues_count = Cache::UserIssueCountService.call(observed_user)
@platform_issues_count = @platform_result["issue-count"].to_i @platform_issues_count = Cache::PlatformIssueCountService.call
# 用户总项目数 # 用户总项目数
@project_count = @user_result["project-count"].to_i @project_count = Cache::UserProjectCountService.call(observed_user)
@platform_project_count = @platform_result["project-count"].to_i @platform_project_count = Cache::PlatformProjectCountService.call
# 用户项目被fork数量 # 用户项目被fork数量
@fork_count = @user_result["fork-count"].to_i @fork_count = Cache::UserProjectForkCountService.call(observed_user)
@platform_fork_count = @platform_result["fork-count"].to_i @platform_fork_count = Cache::PlatformProjectForkCountService.call
# 用户项目关注数 # 用户项目关注数
@project_watchers_count = @user_result["project-watcher-count"].to_i @project_watchers_count = Cache::UserProjectWatchersCountService.call(observed_user)
@platform_project_watchers_count = @platform_result["project-watcher-count"].to_i @platform_project_watchers_count = Cache::PlatformProjectWatchersCountService.call
# 用户项目点赞数 # 用户项目点赞数
@project_praises_count = @user_result["project-praise-count"].to_i @project_praises_count = Cache::UserProjectPraisesCountService.call(observed_user)
@platform_project_praises_count = @platform_result["project-praise-count"].to_i @platform_project_praises_count = Cache::PlatformProjectPraisesCountService.call
# 用户不同语言项目数量 # 用户不同语言项目数量
@project_languages_count = JSON.parse(@user_result["project-language"]) @project_languages_count = Cache::UserProjectLanguagesCountService.call(observed_user)
@platform_project_languages_count = JSON.parse(@platform_result["project-language"]) @platform_project_languages_count = Cache::PlatformProjectLanguagesCountService.call
end end
end end
end end

View File

@ -1,15 +0,0 @@
class Users::SystemNotificationHistoriesController < Users::BaseController
before_action :private_user_resources!, only: [:create]
def create
@history = observed_user.system_notification_histories.new(system_notification_id: params[:system_notification_id])
if @history.save
render_ok
else
Rails.logger.info @history.errors.as_json
render_error(@history.errors.full_messages.join(","))
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
end

View File

@ -1,324 +1,347 @@
class UsersController < ApplicationController class UsersController < ApplicationController
include ApplicationHelper include ApplicationHelper
include Ci::DbConnectable include Ci::DbConnectable
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard] before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard] before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :require_login, only: %i[me list sync_user_info] before_action :require_login, only: %i[me list]
before_action :connect_to_ci_db, only: [:get_user_info] before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update, :update_image] before_action :convert_image!, only: [:update]
skip_before_action :check_sign, only: [:attachment_show] skip_before_action :check_sign, only: [:attachment_show]
def connect_to_ci_db(options={}) def connect_to_ci_db(options={})
if !(current_user && !current_user.is_a?(AnonymousUser) && current_user.devops_certification?) if !(current_user && !current_user.is_a?(AnonymousUser) && current_user.devops_certification?)
return return
end end
if current_user.ci_cloud_account.server_type == Ci::CloudAccount::SERVER_TYPE_TRUSTIE if current_user.ci_cloud_account.server_type == Ci::CloudAccount::SERVER_TYPE_TRUSTIE
connect_to_trustie_ci_database(options) connect_to_trustie_ci_database(options)
else else
connect_to_ci_database(options) connect_to_ci_database(options)
end end
end end
def list def list
scope = User.active.recent.like(params[:search]).includes(:user_extension) scope = User.active.recent.like(params[:search]).includes(:user_extension)
@total_count = scope.size @total_count = scope.size
@users = paginate(scope) @users = paginate(scope)
end end
def show def show
#待办事项,现在未做 #待办事项,现在未做
if User.current.admin? || User.current.login == @user.login if User.current.admin? || User.current.login == @user.login
@waiting_applied_messages = @user.applied_messages.waiting @waiting_applied_messages = @user.applied_messages.waiting
@common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common
@common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common @common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common
#@undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size
@undo_events = @common_applied_transfer_projects.size + @common_applied_projects.size else
else @waiting_applied_messages = AppliedMessage.none
@waiting_applied_messages = AppliedMessage.none @common_applied_transfer_projects = AppliedTransferProject.none
@common_applied_transfer_projects = AppliedTransferProject.none @common_applied_projects = AppliedProject.none
@common_applied_projects = AppliedProject.none @undo_events = 0
@undo_events = 0 end
end #用户的组织数量
#用户的组织数量 # @user_composes_count = @user.composes.size
# @user_composes_count = @user.composes.size @user_composes_count = 0
@user_composes_count = 0 user_organizations = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)) + @user.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @user.organizations.with_visibility("common")
user_organizations = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)) + @user.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @user.organizations.with_visibility("common") @user_org_count = user_organizations.size
@user_org_count = user_organizations.size normal_projects = Project.members_projects(@user.id).to_sql
normal_projects = Project.members_projects(@user.id).to_sql org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql
org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible
user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible @projects_common_count = user_projects.common.size
@projects_common_count = user_projects.common.size @projects_mirrior_count = user_projects.mirror.size
@projects_mirrior_count = user_projects.mirror.size @projects_sync_mirrior_count = user_projects.sync_mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size end
# 为了缓存活跃用户的基本信息,后续删除
Cache::V2::OwnerCommonService.new(@user.id).read def watch_users
end watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc")
if params[:search].present?
def watch_users search_user_ids = User.where(id: watchers.pluck(:watchable_id)).like(params[:search]).pluck(:id)
watchers = Watcher.watching_users(@user.id).includes(:user).order("watchers.created_at desc") watchers = watchers.where(watchable_id: search_user_ids)
if params[:search].present? end
search_user_ids = User.where(id: watchers.pluck(:watchable_id)).like(params[:search]).pluck(:id) @watchers_count = watchers.size
watchers = watchers.where(watchable_id: search_user_ids) @watchers = paginate(watchers)
end end
@watchers_count = watchers.size
@watchers = paginate(watchers) def fan_users
end watchers = @user.watchers.includes(:user).order("watchers.created_at desc")
watchers = watchers.joins(:user).merge(User.like(params[:search]))
def fan_users @watchers_count = watchers.size
watchers = @user.watchers.includes(:user).order("watchers.created_at desc") @watchers = paginate(watchers)
watchers = watchers.joins(:user).merge(User.like(params[:search])) end
@watchers_count = watchers.size
@watchers = paginate(watchers) def hovercard
end end
def hovercard def update
end return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
def update Util.write_file(@image, avatar_path(@user)) if user_params[:image].present?
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id]) @user.attributes = user_params.except(:image)
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id) unless @user.save
Util.write_file(@image, avatar_path(@user)) if user_params[:image].present? render_error(@user.errors.full_messages.join(", "))
@user.attributes = user_params.except(:image) end
unless @user.save end
render_error(-1, @user.errors.full_messages.join(", "))
end def me
end @user = current_user
end
def update_image
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id]) # 贴吧获取用户信接口
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id) def get_user_info
begin
Util.write_file(@image, avatar_path(@user)) @user = current_user
return render_ok({message: '头像修改成功'}) begin
rescue Exception => e result = Notice::Read::CountService.call(current_user.id)
uid_logger_error(e.message) @message_unread_total = result.nil? ? 0 : result[2]["unread_notification"]
render_error(-1, '头像修改失败!') rescue
end @message_unread_total = 0
end
def me # TODO 等消息上线再打开注释
@user = current_user #@tidding_count = unviewed_tiddings(current_user) if current_user.present?
end rescue Exception => e
uid_logger_error(e.message)
# 贴吧获取用户信接口 missing_template
def get_user_info end
begin
@user = current_user end
begin
result = Notice::Read::CountService.call(current_user.id) def attachment_show
@message_unread_total = result.nil? ? 0 : result[2]["unread_notification"] file_name = params[:file_name]
rescue path = params[:path] || file_storage_directory
@message_unread_total = 0 send_file "#{path}/#{file_name}", :filename => "#{file_name}",
end :type => 'game',
# TODO 等消息上线再打开注释 :disposition => 'attachment' #inline can open in browser
#@tidding_count = unviewed_tiddings(current_user) if current_user.present? end
rescue Exception => e
uid_logger_error(e.message) def html_show
missing_template @contents = File.read("#{params[:path]}")
end respond_to do |format|
format.html {render :layout => false}
end end
end
def attachment_show
file_name = params[:file_name] # Redo: 消息总数缓存
path = params[:path] || file_storage_directory def get_navigation_info
send_file "#{path}/#{file_name}", :filename => "#{file_name}", # @old_domain = edu_setting('old_edu_host')
:type => 'game', # @user = current_user
:disposition => 'attachment' #inline can open in browser # # 新消息数
end # @new_message = @user.tidings.where("created_at > '#{@user.click_time}'").count > 0 || @user.private_messages.where("created_at > '#{@user.click_time}'").count > 0
#
def html_show # @user_url = "/users/#{@user.login}"
@contents = File.read("#{params[:path]}") # @career = Career.where(status: true).order("created_at asc").pluck(:id, :name)
respond_to do |format| # @auth = User.current.ec_school.present? ? "#{@old_domain}/ecs/department?school_id=#{User.current.ec_school}" : nil
format.html {render :layout => false} end
end
end # 用户回复功能
def reply_message
# Redo: 消息总数缓存 message = JournalsForMessage.new(reply_message_params)
def get_navigation_info message.user_id = current_user.id
# @old_domain = edu_setting('old_edu_host') message.save!
# @user = current_user
# # 新消息数 render_ok(id: message.id)
# @new_message = @user.tidings.where("created_at > '#{@user.click_time}'").count > 0 || @user.private_messages.where("created_at > '#{@user.click_time}'").count > 0 end
#
# @user_url = "/users/#{@user.login}" # 搜索用户具有管理员角色的项目
# @career = Career.where(status: true).order("created_at asc").pluck(:id, :name) def search_user_projects
# @auth = User.current.ec_school.present? ? "#{@old_domain}/ecs/department?school_id=#{User.current.ec_school}" : nil projects = Project.where.not(status: 9)
end
projects = projects.joins(members: :member_roles).where(member_roles: { role_id: 3 })
# 用户回复功能 projects = projects.where(members: { user_id: current_user.id })
def reply_message
message = JournalsForMessage.new(reply_message_params) search = params[:search].to_s.strip
message.user_id = current_user.id projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
message.save!
@projects = projects.select(:id, :name)
render_ok(id: message.id) end
end
#TODO 个人主页信息forge上弃用-hs, 0602
# 搜索用户具有管理员角色的项目 def homepage_info
def search_user_projects #待办事项,现在未做
projects = Project.where.not(status: 9) @undo_events = 10
#用户的组织数量
projects = projects.joins(members: :member_roles).where(member_roles: { role_id: 3 }) # @user_composes_count = @user.composes.size
projects = projects.where(members: { user_id: current_user.id }) @user_composes_count = 10
end
search = params[:search].to_s.strip
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present? def brief_introduction
content = params[:content].to_s.strip
@projects = projects.select(:id, :name)
end current_user.user_extension.update!(brief_introduction: content)
#TODO 个人主页信息forge上弃用-hs, 0602 render_ok
def homepage_info end
#待办事项,现在未做
@undo_events = 10 def attendance
#用户的组织数量 attendance = Users::AttendanceService.call(current_user)
# @user_composes_count = @user.composes.size render_ok(grade: current_user.grade, next_gold: attendance.next_gold)
@user_composes_count = 10 rescue Users::AttendanceService::Error => ex
end render_error(ex.message)
end
def brief_introduction
content = params[:content].to_s.strip # 其他平台登录后必须将token同步到forge平台实现sso登录功能
def sync_token
current_user.user_extension.update!(brief_introduction: content) return render_error('未找相关用户!') unless @user
render_ok token = Token.get_or_create_permanent_login_token(@user, 'autologin')
end token.update_column(:value, params[:token])
render_ok
def attendance end
attendance = Users::AttendanceService.call(current_user)
render_ok(grade: current_user.grade, next_gold: attendance.next_gold) def trustie_related_projects
rescue Users::AttendanceService::Error => ex projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc")
render_error(ex.message) projects_json = []
end domain_url = EduSetting.get('host_name') + '/projects'
if projects.present?
# 其他平台登录后必须将token同步到forge平台实现sso登录功能 projects.each do |p|
def sync_token project_url = "/#{p.owner.login}/#{p.identifier}"
return render_error('未找相关用户!') unless @user pj = {
id: p.id,
token = Token.get_or_create_permanent_login_token(@user, 'autologin') name: p.name,
token.update_column(:value, params[:token]) is_public: p.is_public,
render_ok updated_on: p.updated_on.strftime("%Y-%m-%d"),
end status: p.status,
is_member: p.member?(current_user.try(:id)),
def trustie_related_projects owner: {
projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc") name: p.owner.try(:show_real_name),
projects_json = [] login: p.owner.login
domain_url = EduSetting.get('host_name') },
if projects.present? members_count: p&.members.size,
projects.each do |p| issues_count: p.issues_count - p.pull_requests_count,
project_url = "/#{p.owner.login}/#{p.identifier}" commits_count: p&.project_score&.changeset_num.to_i,
pj = { http_url: domain_url + project_url,
id: p.id, http_collaborator_url: domain_url + project_url + "/setting/collaborator",
name: p.name, http_issues_url: domain_url + project_url + "/issues",
is_public: p.is_public, http_commits_url: domain_url + project_url + "/commits",
updated_on: p.updated_on.strftime("%Y-%m-%d"), project_score: p&.project_score.present? ? p&.project_score&.as_json(:except=>[:created_at, :updated_at]).merge!(commit_time: format_time(p&.project_score&.commit_time)) : {}
status: p.status, }
is_member: p.member?(current_user.try(:id)), projects_json.push(pj)
owner: { end
name: p.owner.try(:show_real_name), end
login: p.owner.login Rails.logger.info("==========projects_json========+########{projects_json}")
}, render json: { projects: projects_json.present? ? projects_json : {} }
members_count: p&.members.size, end
issues_count: p.issues_count - p.pull_requests_count,
commits_count: p&.project_score&.changeset_num.to_i, def trustie_projects
http_url: domain_url + project_url, user_id = User.select(:id, :login).where(login: params[:login])&.first&.id
http_collaborator_url: domain_url + project_url + "/setting/collaborator", projects = Project.visible
http_issues_url: domain_url + project_url + "/issues",
http_commits_url: domain_url + project_url + "/commits", projects = projects.joins(:members).where(members: { user_id: user_id })
project_score: p&.project_score.present? ? p&.project_score&.as_json(:except=>[:created_at, :updated_at]).merge!(commit_time: format_time(p&.project_score&.commit_time)) : {}
} search = params[:search].to_s.strip
projects_json.push(pj) projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present?
end
end projects = projects.select(:id, :name).limit(10).as_json
Rails.logger.info("==========projects_json========+########{projects_json}") render json: { projects: projects }
render json: { projects: projects_json.present? ? projects_json : {} } end
end
def projects
def trustie_projects is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
user_id = User.select(:id, :login).where(login: params[:login])&.first&.id scope = Projects::ListMyQuery.call(params, @user,is_current_admin_user)
projects = Project.visible @total_count = scope.size
@projects = paginate(scope)
projects = projects.joins(:members).where(members: { user_id: user_id }) end
search = params[:search].to_s.strip # TODO 其他平台登录时同步修改gitea平台对应用户的密码
projects = projects.where('projects.name LIKE ?', "%#{search}%") if search.present? # 该方法主要用于别的平台初次部署对接forge平台同步用户后gitea平台对应的用户密码与forge平台用户密码不一致是问题
def sync_gitea_pwd
projects = projects.select(:id, :name).limit(10).as_json return render_error("未找到相关的用户") if @user.blank?
render json: { projects: projects }
end flag = sync_pwd_to_gitea!(@user, {password: params[:password].to_s})
flag ? render_ok : render_error('同步失败!')
def projects end
is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
scope = Projects::ListMyQuery.call(params, @user,is_current_admin_user) # TODO
@total_count = scope.size # 同步trusite平台用户的salt信息只需同步一次同步完成后该方法可以删除
@projects = paginate(scope) def sync_salt
end user = User.find_by_login params[:login]
return if user.blank?
# TODO 其他平台登录时同步修改gitea平台对应用户的密码 user.update_column(:salt, params[:salt])
# 该方法主要用于别的平台初次部署对接forge平台同步用户后gitea平台对应的用户密码与forge平台用户密码不一致是问题 render_ok
def sync_gitea_pwd end
return render_error("未找到相关的用户") if @user.blank?
# TODO: For Educoder
flag = sync_pwd_to_gitea!(@user, {password: params[:password].to_s}) def change_password
flag ? render_ok : render_error('同步失败!') user = User.find_by_login params[:login]
end return render_error("用户 #{params[:login]} 不存在.") if user.nil?
# TODO form_params= {
# 同步trusite平台用户的salt信息只需同步一次同步完成后该方法可以删除 login: params[:login],
def sync_salt email: user&.mail,
user = User.find_by_login params[:login] password: params[:password],
return if user.blank? user: user
user.update_column(:salt, params[:salt]) }
render_ok Gitea::User::ChangePasswordForm.new(form_params).validate!
end
sync_params = {
def sync_user_info email: user&.mail,
user = User.find_by_login params[:login] password: params[:password]
return render_forbidden unless user === current_user }
sync_params = { if sync_params.present?
email: params[:email], interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
password: params[:password] if interactor.success?
} user.update!(password: params[:password], is_sync_pwd: true)
render_ok
Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate! else
render_error(interactor.error)
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params) end
if interactor.success? end
user.update!(password: params[:password], mail: params[:email], status: User::STATUS_ACTIVE) end
render_ok
else # TODO: For Educoder
render_error(interactor.error) def change_email
end user = User.find_by_login params[:login]
end return render_error("用户 #{rq_params[:login]} 不存在.") if user.nil?
private form_params= {
def load_user login: params[:login],
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id]) email: user&.mail,
end user: user
}
def user_params
params.require(:user).permit(:nickname, :image, Gitea::User::ChangeEmailForm.new(form_params).validate!
user_extension_attributes: [
:gender, :location, :location_city, sync_params = {
:occupation, :technical_title, email: params[:email]
:school_id, :department_id, :province, :city, }
:custom_department, :identity, :student_id, :description,
:show_email, :show_location, :show_department] if sync_params.present?
) interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
end if interactor.success?
user.update!(mail: params[:email])
def reply_message_params render_ok
normal_status(-1, "参数不对") if params[:journals_for_message][:jour_type].nil? || params[:journals_for_message][:jour_id].nil? || else
params[:journals_for_message][:notes].nil? || params[:journals_for_message][:reply_id].nil? render_error(interactor.error)
params.require(:journals_for_message).permit(:jour_type, :jour_id, :notes, :m_parent_id, :reply_id) end
end end
end
def check_user_exist
return if @user.present? private
render_not_found def load_user
end @user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
end
end
def user_params
params.require(:user).permit(:nickname, :image,
user_extension_attributes: [
:gender, :location, :location_city,
:occupation, :technical_title,
:school_id, :department_id, :province, :city,
:custom_department, :identity, :student_id, :description,
:show_email, :show_location, :show_department]
)
end
def reply_message_params
normal_status(-1, "参数不对") if params[:journals_for_message][:jour_type].nil? || params[:journals_for_message][:jour_id].nil? ||
params[:journals_for_message][:notes].nil? || params[:journals_for_message][:reply_id].nil?
params.require(:journals_for_message).permit(:jour_type, :jour_id, :notes, :m_parent_id, :reply_id)
end
def check_user_exist
return if @user.present?
render_not_found
end
end

View File

@ -0,0 +1,5 @@
module CourseDecorator
def can_visited?
is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self)
end
end

View File

@ -0,0 +1,2 @@
module EcCourseTargetDecorator
end

View File

@ -0,0 +1,16 @@
module ExperienceDecorator
def container_type_text
I18n.t("experience.container_type.#{container_type.to_s.underscore}")
end
def content
case container_type.to_s.underscore
when 'game' then
game = Game.find_by(id: container_id)
game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
when 'shixun_publish' then
shixun = Shixun.find_by(id: container_id)
shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
end
end
end

View File

@ -0,0 +1,39 @@
module GradeDecorator
def container_type_text
I18n.t("grade.container_type.#{container_type.to_s.underscore}")
end
def content
case container_type.to_s.underscore
when 'avatar' then '用户首次上传头像获得的奖励'
when 'phone' then '用户首次绑定手机号码获得的奖励'
when 'mail' then '用户首次绑定邮箱获得的奖励'
when 'attendance' then '用户每天签到获得的奖励'
when 'account' then '新用户首次填写基本资料获得的奖励'
when 'memo' then '发布的评论或者帖子获得平台奖励'
when 'discusses' then '发布的评论获得平台奖励'
when 'star' then '用户给实训评分获得的随机奖励'
when 'feedback' then '反馈的问题获得平台奖励'
when 'authentication' then '用户首次完成实名认证获得的奖励'
when 'professional' then '用户首次完成职业认证获得的奖励'
when 'answer' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的参考答案消耗的金币" : ''
when 'game' then
game = Game.find_by(id: container_id)
game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
when 'test_set' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关的隐藏测试集消耗的金币" : ''
when 'shixun_publish' then
shixun = Shixun.find_by(id: container_id)
shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
when 'check_ta_answer' then
game = Game.find_by(id: container_id)
game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的TA人解答消耗的金币" : ''
when 'hack' then
hack = Hack.find_by(id: container_id)
hack.present? ? "完成了题目解答“#{hack.name}”,获得金币奖励:#{hack.score}" : ''
end
end
end

View File

@ -0,0 +1,5 @@
module LibraryDecorator
extend ApplicationDecorator
display_time_method :published_at, :created_at, :updated_at
end

View File

@ -0,0 +1,5 @@
module ShixunDecorator
def human_status
I18n.t("shixun.status.#{status}")
end
end

View File

@ -0,0 +1,5 @@
module SubjectDecorator
def can_visited?
published? || User.current.admin? || member?(User.current)
end
end

View File

@ -0,0 +1,5 @@
module VideoDecorator
extend ApplicationDecorator
display_time_method :published_at, :created_at, :updated_at
end

View File

@ -0,0 +1,2 @@
build/
.github/

View File

@ -0,0 +1,18 @@
# EditorConfig is awesome: https://EditorConfig.org
# Top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.rb]
charset = utf-8
[*.md]
trim_trailing_whitespace = false

1
app/docs/slate/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
source/javascripts/lib/* linguist-vendored

View File

@ -0,0 +1,22 @@
---
name: Report a Bug
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Bug Description**
A clear and concise description of what the bug is and how to reproduce it.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Browser (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Last upstream Slate commit (run `git log --author="Robert Lord" | head -n 1`):**
Put the commit hash here

View File

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions, Ideas, Discussions
url: https://github.com/slatedocs/slate/discussions
about: Ask and answer questions, and propose new features.

View File

@ -0,0 +1,5 @@
<!--
⚠️ 🚨 ⚠️ STOP AND READ THIS ⚠️ 🚨 ⚠️
👆👆 see that 'base fork' dropdown above? You should change it! The default value of "slatedocs/slate" submits your change to ALL USERS OF SLATE, not just your company. This is PROBABLY NOT WHAT YOU WANT.
-->

View File

@ -0,0 +1,42 @@
name: Build
on:
push:
branches: [ '*' ]
pull_request:
branches: [ '*' ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: [2.3, 2.4, 2.5, 2.6, 2.7]
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- uses: actions/cache@v2
with:
path: vendor/bundle
key: gems-${{ runner.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
gems-${{ runner.os }}-${{ matrix.ruby-version }}-
gems-${{ runner.os }}-
# necessary to get ruby 2.3 to work nicely with bundler vendor/bundle cache
# can remove once ruby 2.3 is no longer supported
- run: gem update --system
- run: bundle config set deployment 'true'
- name: bundle install
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- run: bundle exec middleman build

View File

@ -0,0 +1,41 @@
name: Deploy
on:
push:
branches: [ 'main' ]
jobs:
deploy:
runs-on: ubuntu-latest
env:
ruby-version: 2.5
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby-version }}
- uses: actions/cache@v2
with:
path: vendor/bundle
key: gems-${{ runner.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
gems-${{ runner.os }}-${{ matrix.ruby-version }}-
gems-${{ runner.os }}-
- run: bundle config set deployment 'true'
- name: bundle install
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- run: bundle exec middleman build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build
keep_files: true

View File

@ -0,0 +1,50 @@
name: Dev Deploy
on:
push:
branches: [ 'dev' ]
jobs:
deploy:
runs-on: ubuntu-latest
env:
ruby-version: 2.5
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby-version }}
- uses: actions/cache@v2
with:
path: vendor/bundle
key: gems-${{ runner.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
gems-${{ runner.os }}-${{ matrix.ruby-version }}-
gems-${{ runner.os }}-
- run: bundle config set deployment 'true'
- name: bundle install
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- run: bundle exec middleman build
- name: Push to Docker Hub
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_KEY }}
repository: slatedocs/slate
tag_with_ref: true
- name: Deploy
uses: peaceiris/actions-gh-pages@v3.7.0-8
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
destination_dir: dev
publish_dir: ./build
keep_files: true

View File

@ -0,0 +1,22 @@
name: Publish Docker image
on:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Push to Docker Hub
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_KEY }}
repository: slatedocs/slate
tag_with_ref: true
tags: latest

27
app/docs/slate/.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
*.gem
*.rbc
.bundle
.config
coverage
InstalledFiles
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
*.DS_STORE
build/
.cache
.vagrant
.sass-cache
# YARD artifacts
.yardoc
_yardoc
doc/
.idea/
# Vagrant artifacts
ubuntu-*-console.log

254
app/docs/slate/CHANGELOG.md Normal file
View File

@ -0,0 +1,254 @@
# Changelog
## Version 2.8.0
*October 27, 2020*
* Remove last trailing newline when using the copy code button
* Rework docker image and make available at slatedocs/slate
* Improve Dockerfile layout to improve caching (thanks @micvbang)
* Bump rouge from 3.20 to 3.24
* Bump nokogiri from 1.10.9 to 1.10.10
* Bump middleman from 4.3.8 to 4.3.11
* Bump lunr.js from 2.3.8 to 2.3.9
## Version 2.7.1
*August 13, 2020*
* __[security]__ Bumped middleman from 4.3.7 to 4.3.8
_Note_: Slate uses redcarpet, not kramdown, for rendering markdown to HTML, and so was unaffected by the security vulnerability in middleman.
If you have changed slate to use kramdown, and with GFM, you may need to install the `kramdown-parser-gfm` gem.
## Version 2.7.0
*June 21, 2020*
* __[security]__ Bumped rack in Gemfile.lock from 2.2.2 to 2.2.3
* Bumped bundled jQuery from 3.2.1 to 3.5.1
* Bumped bundled lunr from 0.5.7 to 2.3.8
* Bumped imagesloaded from 3.1.8 to 4.1.4
* Bumped rouge from 3.17.0 to 3.20.0
* Bumped redcarpet from 3.4.0 to 3.5.0
* Fix color of highlighted code being unreadable when printing page
* Add clipboard icon for "Copy to Clipboard" functionality to code boxes (see note below)
* Fix handling of ToC selectors that contain punctutation (thanks @gruis)
* Fix language bar truncating languages that overflow screen width
* Strip HTML tags from ToC title before displaying it in title bar in JS (backup to stripping done in Ruby code) (thanks @atic)
To enable the new clipboard icon, you need to add `code_clipboard: true` to the frontmatter of source/index.html.md.
See [this line](https://github.com/slatedocs/slate/blame/main/source/index.html.md#L19) for an example of usage.
## Version 2.6.1
*May 30, 2020*
* __[security]__ update child dependency activesupport in Gemfile.lock to 5.4.2.3
* Update Middleman in Gemfile.lock to 4.3.7
* Replace Travis-CI with GitHub actions for continuous integration
* Replace Spectrum with GitHub discussions
## Version 2.6.0
*May 18, 2020*
__Note__: 2.5.0 was "pulled" due to a breaking bug discovered after release. It is recommended to skip it, and move straight to 2.6.0.
* Fix large whitespace gap in middle column for sections with codeblocks
* Fix highlighted code elements having a different background than rest of code block
* Change JSON keys to have a different font color than their values
* Disable asset hashing for woff and woff2 elements due to middleman bug breaking woff2 asset hashing in general
* Move Dockerfile to Debian from Alpine
* Converted repo to a [GitHub template](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository)
* Update sassc to 2.3.0 in Gemfile.lock
## Version 2.5.0
*May 8, 2020*
* __[security]__ update nokogiri to ~> 1.10.8
* Update links in example docs to https://github.com/slatedocs/slate from https://github.com/lord/slate
* Update LICENSE to include full Apache 2.0 text
* Test slate against Ruby 2.5 and 2.6 on Travis-CI
* Update Vagrantfile to use Ubuntu 18.04 (thanks @bradthurber)
* Parse arguments and flags for deploy.sh on script start, instead of potentially after building source files
* Install nodejs inside Vagrantfile (thanks @fernandoaguilar)
* Add Dockerfile for running slate (thanks @redhatxl)
* update middleman-syntax and rouge to ~>3.2
* update middleman to 4.3.6
## Version 2.4.0
*October 19, 2019*
- Move repository from lord/slate to slatedocs/slate
- Fix documentation to point at new repo link, thanks to [Arun](https://github.com/slash-arun), [Gustavo Gawryszewski](https://github.com/gawry), and [Daniel Korbit](https://github.com/danielkorbit)
- Update `nokogiri` to 1.10.4
- Update `ffi` in `Gemfile.lock` to fix security warnings, thanks to [Grey Baker](https://github.com/greysteil) and [jakemack](https://github.com/jakemack)
- Update `rack` to 2.0.7 in `Gemfile.lock` to fix security warnings, thanks to [Grey Baker](https://github.com/greysteil) and [jakemack](https://github.com/jakemack)
- Update middleman to `4.3` and relax constraints on middleman related gems, thanks to [jakemack](https://github.com/jakemack)
- Add sass gem, thanks to [jakemack](https://github.com/jakemack)
- Activate `asset_cache` in middleman to improve cacheability of static files, thanks to [Sam Gilman](https://github.com/thenengah)
- Update to using bundler 2 for `Gemfile.lock`, thanks to [jakemack](https://github.com/jakemack)
## Version 2.3.1
*July 5, 2018*
- Update `sprockets` in `Gemfile.lock` to fix security warnings
## Version 2.3
*July 5, 2018*
- Allows strikethrough in markdown by default.
- Upgrades jQuery to 3.2.1, thanks to [Tomi Takussaari](https://github.com/TomiTakussaari)
- Fixes invalid HTML in `layout.erb`, thanks to [Eric Scouten](https://github.com/scouten) for pointing out
- Hopefully fixes Vagrant memory issues, thanks to [Petter Blomberg](https://github.com/p-blomberg) for the suggestion
- Cleans HTML in headers before setting `document.title`, thanks to [Dan Levy](https://github.com/justsml)
- Allows trailing whitespace in markdown files, thanks to [Samuel Cousin](https://github.com/kuzyn)
- Fixes pushState/replaceState problems with scrolling not changing the document hash, thanks to [Andrey Fedorov](https://github.com/anfedorov)
- Removes some outdated examples, thanks [@al-tr](https://github.com/al-tr), [Jerome Dahdah](https://github.com/jdahdah), and [Ricardo Castro](https://github.com/mccricardo)
- Fixes `nav-padding` bug, thanks [Jerome Dahdah](https://github.com/jdahdah)
- Code style fixes thanks to [Sebastian Zaremba](https://github.com/vassyz)
- Nokogiri version bump thanks to [Grey Baker](https://github.com/greysteil)
- Fix to default `index.md` text thanks to [Nick Busey](https://github.com/NickBusey)
Thanks to everyone who contributed to this release!
## Version 2.2
*January 19, 2018*
- Fixes bugs with some non-roman languages not generating unique headers
- Adds editorconfig, thanks to [Jay Thomas](https://github.com/jaythomas)
- Adds optional `NestingUniqueHeadCounter`, thanks to [Vladimir Morozov](https://github.com/greenhost87)
- Small fixes to typos and language, thx [Emir Ribić](https://github.com/ribice), [Gregor Martynus](https://github.com/gr2m), and [Martius](https://github.com/martiuslim)!
- Adds links to Spectrum chat for questions in README and ISSUE_TEMPLATE
## Version 2.1
*October 30, 2017*
- Right-to-left text stylesheet option, thanks to [Mohammad Hossein Rabiee](https://github.com/mhrabiee)
- Fix for HTML5 history state bug, thanks to [Zach Toolson](https://github.com/ztoolson)
- Small styling changes, typo fixes, small bug fixes from [Marian Friedmann](https://github.com/rnarian), [Ben Wilhelm](https://github.com/benwilhelm), [Fouad Matin](https://github.com/fouad), [Nicolas Bonduel](https://github.com/NicolasBonduel), [Christian Oliff](https://github.com/coliff)
Thanks to everyone who submitted PRs for this version!
## Version 2.0
*July 17, 2017*
- All-new statically generated table of contents
- Should be much faster loading and scrolling for large pages
- Smaller Javascript file sizes
- Avoids the problem with the last link in the ToC not ever highlighting if the section was shorter than the page
- Fixes control-click not opening in a new page
- Automatically updates the HTML title as you scroll
- Updated design
- New default colors!
- New spacings and sizes!
- System-default typefaces, just like GitHub
- Added search input delay on large corpuses to reduce lag
- We even bumped the major version cause hey, why not?
- Various small bug fixes
Thanks to everyone who helped debug or wrote code for this version! It was a serious community effort, and I couldn't have done it alone.
## Version 1.5
*February 23, 2017*
- Add [multiple tabs per programming language](https://github.com/lord/slate/wiki/Multiple-language-tabs-per-programming-language) feature
- Upgrade Middleman to add Ruby 1.4.0 compatibility
- Switch default code highlighting color scheme to better highlight JSON
- Various small typo and bug fixes
## Version 1.4
*November 24, 2016*
- Upgrade Middleman and Rouge gems, should hopefully solve a number of bugs
- Update some links in README
- Fix broken Vagrant startup script
- Fix some problems with deploy.sh help message
- Fix bug with language tabs not hiding properly if no error
- Add `!default` to SASS variables
- Fix bug with logo margin
- Bump tested Ruby versions in .travis.yml
## Version 1.3.3
*June 11, 2016*
Documentation and example changes.
## Version 1.3.2
*February 3, 2016*
A small bugfix for slightly incorrect background colors on code samples in some cases.
## Version 1.3.1
*January 31, 2016*
A small bugfix for incorrect whitespace in code blocks.
## Version 1.3
*January 27, 2016*
We've upgraded Middleman and a number of other dependencies, which should fix quite a few bugs.
Instead of `rake build` and `rake deploy`, you should now run `bundle exec middleman build --clean` to build your server, and `./deploy.sh` to deploy it to Github Pages.
## Version 1.2
*June 20, 2015*
**Fixes:**
- Remove crash on invalid languages
- Update Tocify to scroll to the highlighted header in the Table of Contents
- Fix variable leak and update search algorithms
- Update Python examples to be valid Python
- Update gems
- More misc. bugfixes of Javascript errors
- Add Dockerfile
- Remove unused gems
- Optimize images, fonts, and generated asset files
- Add chinese font support
- Remove RedCarpet header ID patch
- Update language tabs to not disturb existing query strings
## Version 1.1
*July 27, 2014*
**Fixes:**
- Finally, a fix for the redcarpet upgrade bug
## Version 1.0
*July 2, 2014*
[View Issues](https://github.com/tripit/slate/issues?milestone=1&state=closed)
**Features:**
- Responsive designs for phones and tablets
- Started tagging versions
**Fixes:**
- Fixed 'unrecognized expression' error
- Fixed #undefined hash bug
- Fixed bug where the current language tab would be unselected
- Fixed bug where tocify wouldn't highlight the current section while searching
- Fixed bug where ids of header tags would have special characters that caused problems
- Updated layout so that pages with disabled search wouldn't load search.js
- Cleaned up Javascript

View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@lord.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

12
app/docs/slate/Gemfile Normal file
View File

@ -0,0 +1,12 @@
ruby '>=2.3.1'
source 'https://gems.ruby-china.com'
# Middleman
gem 'middleman', '~>4.3'
gem 'middleman-syntax', '~> 3.2'
gem 'middleman-autoprefixer', '~> 2.7'
gem 'middleman-sprockets', '~> 4.1'
gem 'rouge', '~> 3.21'
gem 'redcarpet', '~> 3.5.0'
gem 'nokogiri', '~> 1.10.8'
gem 'sass'

136
app/docs/slate/Gemfile.lock Normal file
View File

@ -0,0 +1,136 @@
GEM
remote: https://gems.ruby-china.com/
specs:
activesupport (5.2.4.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
autoprefixer-rails (9.5.1.1)
execjs
backports (3.18.2)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.1.7)
contracts (0.13.0)
dotenv (2.7.6)
erubis (2.7.0)
execjs (2.7.0)
fast_blank (1.0.0)
fastimage (2.2.0)
ffi (1.13.1)
haml (5.1.2)
temple (>= 0.8.0)
tilt
hamster (3.0.0)
concurrent-ruby (~> 1.0)
hashie (3.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
kramdown (2.3.0)
rexml
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
memoist (0.16.2)
middleman (4.3.11)
coffee-script (~> 2.2)
haml (>= 4.0.5)
kramdown (>= 2.3.0)
middleman-cli (= 4.3.11)
middleman-core (= 4.3.11)
middleman-autoprefixer (2.10.1)
autoprefixer-rails (~> 9.1)
middleman-core (>= 3.3.3)
middleman-cli (4.3.11)
thor (>= 0.17.0, < 2.0)
middleman-core (4.3.11)
activesupport (>= 4.2, < 6.0)
addressable (~> 2.3)
backports (~> 3.6)
bundler
contracts (~> 0.13.0)
dotenv
erubis
execjs (~> 2.0)
fast_blank
fastimage (~> 2.0)
hamster (~> 3.0)
hashie (~> 3.4)
i18n (~> 0.9.0)
listen (~> 3.0.0)
memoist (~> 0.14)
padrino-helpers (~> 0.13.0)
parallel
rack (>= 1.4.5, < 3)
sassc (~> 2.0)
servolux
tilt (~> 2.0.9)
uglifier (~> 3.0)
middleman-sprockets (4.1.1)
middleman-core (~> 4.0)
sprockets (>= 3.0)
middleman-syntax (3.2.0)
middleman-core (>= 3.2)
rouge (~> 3.2)
mini_portile2 (2.4.0)
minitest (5.14.2)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
padrino-helpers (0.13.3.4)
i18n (~> 0.6, >= 0.6.7)
padrino-support (= 0.13.3.4)
tilt (>= 1.4.1, < 3)
padrino-support (0.13.3.4)
activesupport (>= 3.1)
parallel (1.19.2)
public_suffix (4.0.6)
rack (2.2.3)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
redcarpet (3.5.0)
rexml (3.2.4)
rouge (3.24.0)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sassc (2.4.0)
ffi (~> 1.9)
servolux (0.13.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
temple (0.8.2)
thor (1.0.1)
thread_safe (0.3.6)
tilt (2.0.10)
tzinfo (1.2.7)
thread_safe (~> 0.1)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
PLATFORMS
ruby
DEPENDENCIES
middleman (~> 4.3)
middleman-autoprefixer (~> 2.7)
middleman-sprockets (~> 4.1)
middleman-syntax (~> 3.2)
nokogiri (~> 1.10.8)
redcarpet (~> 3.5.0)
rouge (~> 3.21)
sass
RUBY VERSION
ruby 2.3.3p222
BUNDLED WITH
2.1.4

201
app/docs/slate/LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

81
app/docs/slate/README.md Normal file
View File

@ -0,0 +1,81 @@
<p align="center">
<img src="https://raw.githubusercontent.com/slatedocs/img/main/logo-slate.png" alt="Slate: API Documentation Generator" width="226">
<br>
<a href="https://github.com/slatedocs/slate/actions?query=workflow%3ABuild+branch%3Amain"><img src="https://github.com/slatedocs/slate/workflows/Build/badge.svg?branch=main" alt="Build Status"></a>
</p>
<p align="center">Slate helps you create beautiful, intelligent, responsive API documentation.</p>
<p align="center"><img src="https://raw.githubusercontent.com/slatedocs/img/main/screenshot-slate.png" width=700 alt="Screenshot of Example Documentation created with Slate"></p>
<p align="center"><em>The example above was created with Slate. Check it out at <a href="https://slatedocs.github.io/slate">slatedocs.github.io/slate</a>.</em></p>
Features
------------
* **Clean, intuitive design** — With Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [PayPal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even in print.
* **Everything on a single page** — Gone are the days when your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy.
* **Slate is just Markdown** — When you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks.
* **Write code samples in multiple languages** — If your API has bindings in multiple programming languages, you can easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with GitHub Flavored Markdown.
* **Out-of-the-box syntax highlighting** for [over 100 languages](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers), no configuration required.
* **Automatic, smoothly scrolling table of contents** on the far left of the page. As you scroll, it displays your current position in the document. It's fast, too. We're using Slate at TripIt to build documentation for our new API, where our table of contents has over 180 entries. We've made sure that the performance remains excellent, even for larger documents.
* **Let your users update your documentation for you** — By default, your Slate-generated documentation is hosted in a public GitHub repository. Not only does this mean you get free hosting for your docs with GitHub Pages, but it also makes it simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to use GitHub, you're also welcome to host your docs elsewhere.
* **RTL Support** Full right-to-left layout for RTL languages such as Arabic, Persian (Farsi), Hebrew etc.
Getting started with Slate is super easy! Simply press the green "use this template" button above and follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](https://slatedocs.github.io/slate/).
Getting Started with Slate
------------------------------
To get started with Slate, please check out the [Getting Started](https://github.com/slatedocs/slate/wiki#getting-started)
section in our [wiki](https://github.com/slatedocs/slate/wiki).
We support running Slate in three different ways:
* [Natively](https://github.com/slatedocs/slate/wiki/Using-Slate-Natively)
* [Using Vagrant](https://github.com/slatedocs/slate/wiki/Using-Slate-in-Vagrant)
* [Using Docker](https://github.com/slatedocs/slate/wiki/Using-Slate-in-Docker)
Companies Using Slate
---------------------------------
* [NASA](https://api.nasa.gov)
* [Sony](http://developers.cimediacloud.com)
* [Best Buy](https://bestbuyapis.github.io/api-documentation/)
* [Travis-CI](https://docs.travis-ci.com/api/)
* [Greenhouse](https://developers.greenhouse.io/harvest.html)
* [WooCommerce](http://woocommerce.github.io/woocommerce-rest-api-docs/)
* [Dwolla](https://docs.dwolla.com/)
* [Clearbit](https://clearbit.com/docs)
* [Coinbase](https://developers.coinbase.com/api)
* [Parrot Drones](http://developer.parrot.com/docs/bebop/)
* [Scale](https://docs.scaleapi.com/)
You can view more in [the list on the wiki](https://github.com/slatedocs/slate/wiki/Slate-in-the-Wild).
Questions? Need Help? Found a bug?
--------------------
If you've got questions about setup, deploying, special feature implementation in your fork, or just want to chat with the developer, please feel free to [start a thread in our Discussions tab](https://github.com/slatedocs/slate/discussions)!
Found a bug with upstream Slate? Go ahead and [submit an issue](https://github.com/slatedocs/slate/issues). And, of course, feel free to submit pull requests with bug fixes or changes to the `dev` branch.
Contributors
--------------------
Slate was built by [Robert Lord](https://lord.io) while at [TripIt](https://www.tripit.com/). The project is now maintained by [Matthew Peveler](https://github.com/MasterOdin) and [Mike Ralphson](https://github.com/MikeRalphson).
Thanks to the following people who have submitted major pull requests:
- [@chrissrogers](https://github.com/chrissrogers)
- [@bootstraponline](https://github.com/bootstraponline)
- [@realityking](https://github.com/realityking)
- [@cvkef](https://github.com/cvkef)
Also, thanks to [Sauce Labs](http://saucelabs.com) for sponsoring the development of the responsive styles.

46
app/docs/slate/Vagrantfile vendored Normal file
View File

@ -0,0 +1,46 @@
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/bionic64"
config.vm.network :forwarded_port, guest: 4567, host: 4567
config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
end
config.vm.provision "bootstrap",
type: "shell",
inline: <<-SHELL
# add nodejs v12 repository
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get update
sudo apt-get install -yq ruby ruby-dev
sudo apt-get install -yq pkg-config build-essential nodejs git libxml2-dev libxslt-dev
sudo apt-get autoremove -yq
gem install --no-ri --no-rdoc bundler
SHELL
# add the local user git config to the vm
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
config.vm.provision "install",
type: "shell",
privileged: false,
inline: <<-SHELL
echo "=============================================="
echo "Installing app dependencies"
cd /vagrant
bundle config build.nokogiri --use-system-libraries
bundle install
SHELL
config.vm.provision "run",
type: "shell",
privileged: false,
run: "always",
inline: <<-SHELL
echo "=============================================="
echo "Starting up middleman at http://localhost:4567"
echo "If it does not come up, check the ~/middleman.log file for any error messages"
cd /vagrant
bundle exec middleman server --watcher-force-polling --watcher-latency=1 &> ~/middleman.log &
SHELL
end

65
app/docs/slate/config.rb Normal file
View File

@ -0,0 +1,65 @@
set :build_dir, '../../../public/docs/'
# Unique header generation
require './lib/unique_head.rb'
# Markdown
set :markdown_engine, :redcarpet
set :markdown,
fenced_code_blocks: true,
smartypants: true,
disable_indented_code_blocks: true,
prettify: true,
strikethrough: true,
tables: true,
with_toc_data: true,
no_intra_emphasis: true,
renderer: UniqueHeadCounter
# Assets
set :css_dir, 'stylesheets'
set :js_dir, 'javascripts'
set :images_dir, 'images'
set :fonts_dir, 'fonts'
# Activate the syntax highlighter
activate :syntax
ready do
require './lib/monokai_sublime_slate.rb'
require './lib/multilang.rb'
end
activate :sprockets
activate :autoprefixer do |config|
config.browsers = ['last 2 version', 'Firefox ESR']
config.cascade = false
config.inline = true
end
# Github pages require relative links
activate :relative_assets
set :relative_links, true
# Build Configuration
configure :build do
# We do want to hash woff and woff2 as there's a bug where woff2 will use
# woff asset hash which breaks things. Trying to use a combination of ignore and
# rewrite_ignore does not work as it conflicts weirdly with relative_assets. Disabling
# the .woff2 extension only does not work as .woff will still activate it so have to
# have both. See https://github.com/slatedocs/slate/issues/1171 for more details.
activate :asset_hash, :exts => app.config[:asset_extensions] - %w[.woff .woff2]
# If you're having trouble with Middleman hanging, commenting
# out the following two lines has been known to help
activate :minify_css
activate :minify_javascript
# activate :gzip
end
# Deploy Configuration
# If you want Middleman to listen on a different port, you can set that below
set :port, 4567
helpers do
require './lib/toc_data.rb'
end

226
app/docs/slate/deploy.sh Executable file
View File

@ -0,0 +1,226 @@
#!/usr/bin/env bash
set -o errexit #abort if any command fails
me=$(basename "$0")
help_message="\
Usage: $me [-c FILE] [<options>]
Deploy generated files to a git branch.
Options:
-h, --help Show this help information.
-v, --verbose Increase verbosity. Useful for debugging.
-e, --allow-empty Allow deployment of an empty directory.
-m, --message MESSAGE Specify the message used when committing on the
deploy branch.
-n, --no-hash Don't append the source commit's hash to the deploy
commit's message.
--source-only Only build but not push
--push-only Only push but not build
"
run_build() {
bundle exec middleman build --clean
}
parse_args() {
# Set args from a local environment file.
if [ -e ".env" ]; then
source .env
fi
# Parse arg flags
# If something is exposed as an environment variable, set/overwrite it
# here. Otherwise, set/overwrite the internal variable instead.
while : ; do
if [[ $1 = "-h" || $1 = "--help" ]]; then
echo "$help_message"
exit 0
elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
verbose=true
shift
elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
allow_empty=true
shift
elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
commit_message=$2
shift 2
elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
GIT_DEPLOY_APPEND_HASH=false
shift
elif [[ $1 = "--source-only" ]]; then
source_only=true
shift
elif [[ $1 = "--push-only" ]]; then
push_only=true
shift
else
break
fi
done
if [ ${source_only} ] && [ ${push_only} ]; then
>&2 echo "You can only specify one of --source-only or --push-only"
exit 1
fi
# Set internal option vars from the environment and arg flags. All internal
# vars should be declared here, with sane defaults if applicable.
# Source directory & target branch.
deploy_directory=build
deploy_branch=gh-pages
#if no user identity is already set in the current git environment, use this:
default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
default_email=${GIT_DEPLOY_EMAIL:-}
#repository to deploy to. must be readable and writable.
repo=origin
#append commit hash to the end of message by default
append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
}
main() {
enable_expanded_output
if ! git diff --exit-code --quiet --cached; then
echo Aborting due to uncommitted changes in the index >&2
return 1
fi
commit_title=`git log -n 1 --format="%s" HEAD`
commit_hash=` git log -n 1 --format="%H" HEAD`
#default commit message uses last title if a custom one is not supplied
if [[ -z $commit_message ]]; then
commit_message="publish: $commit_title"
fi
#append hash to commit message unless no hash flag was found
if [ $append_hash = true ]; then
commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
fi
previous_branch=`git rev-parse --abbrev-ref HEAD`
if [ ! -d "$deploy_directory" ]; then
echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
return 1
fi
# must use short form of flag in ls for compatibility with macOS and BSD
if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
return 1
fi
if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
# deploy_branch exists in $repo; make sure we have the latest version
disable_expanded_output
git fetch --force $repo $deploy_branch:$deploy_branch
enable_expanded_output
fi
# check if deploy_branch exists locally
if git show-ref --verify --quiet "refs/heads/$deploy_branch"
then incremental_deploy
else initial_deploy
fi
restore_head
}
initial_deploy() {
git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
git --work-tree "$deploy_directory" add --all
commit+push
}
incremental_deploy() {
#make deploy_branch the current branch
git symbolic-ref HEAD refs/heads/$deploy_branch
#put the previously committed contents of deploy_branch into the index
git --work-tree "$deploy_directory" reset --mixed --quiet
git --work-tree "$deploy_directory" add --all
set +o errexit
diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
set -o errexit
case $diff in
0) echo No changes to files in $deploy_directory. Skipping commit.;;
1) commit+push;;
*)
echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to main, use: git symbolic-ref HEAD refs/heads/main && git reset --mixed >&2
return $diff
;;
esac
}
commit+push() {
set_user_id
git --work-tree "$deploy_directory" commit -m "$commit_message"
disable_expanded_output
#--quiet is important here to avoid outputting the repo URL, which may contain a secret token
git push --quiet $repo $deploy_branch
enable_expanded_output
}
#echo expanded commands as they are executed (for debugging)
enable_expanded_output() {
if [ $verbose ]; then
set -o xtrace
set +o verbose
fi
}
#this is used to avoid outputting the repo URL, which may contain a secret token
disable_expanded_output() {
if [ $verbose ]; then
set +o xtrace
set -o verbose
fi
}
set_user_id() {
if [[ -z `git config user.name` ]]; then
git config user.name "$default_username"
fi
if [[ -z `git config user.email` ]]; then
git config user.email "$default_email"
fi
}
restore_head() {
if [[ $previous_branch = "HEAD" ]]; then
#we weren't on any branch before, so just set HEAD back to the commit it was on
git update-ref --no-deref HEAD $commit_hash $deploy_branch
else
git symbolic-ref HEAD refs/heads/$previous_branch
fi
git reset --mixed
}
filter() {
sed -e "s|$repo|\$repo|g"
}
sanitize() {
"$@" 2> >(filter 1>&2) | filter
}
parse_args "$@"
if [[ ${source_only} ]]; then
run_build
elif [[ ${push_only} ]]; then
main "$@"
else
run_build
main "$@"
fi

View File

@ -0,0 +1,148 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M438.857 73.143q119.429 0 220.286 58.857t159.714 159.714 58.857 220.286-58.857 220.286-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857zM512 785.714v-108.571q0-8-5.143-13.429t-12.571-5.429h-109.714q-7.429 0-13.143 5.714t-5.714 13.143v108.571q0 7.429 5.714 13.143t13.143 5.714h109.714q7.429 0 12.571-5.429t5.143-13.429zM510.857 589.143l10.286-354.857q0-6.857-5.714-10.286-5.714-4.571-13.714-4.571h-125.714q-8 0-13.714 4.571-5.714 3.429-5.714 10.286l9.714 354.857q0 5.714 5.714 10t13.714 4.286h105.714q8 0 13.429-4.286t6-10z"
],
"attrs": [],
"isMulticolor": false,
"tags": [
"exclamation-circle"
],
"defaultCode": 61546,
"grid": 14
},
"attrs": [],
"properties": {
"id": 100,
"order": 4,
"prevSize": 28,
"code": 58880,
"name": "exclamation-sign",
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M585.143 786.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-54.857v-292.571q0-8-5.143-13.143t-13.143-5.143h-182.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h54.857v182.857h-54.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h256q8 0 13.143-5.143t5.143-13.143zM512 274.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-109.714q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h109.714q8 0 13.143-5.143t5.143-13.143zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
],
"attrs": [],
"isMulticolor": false,
"tags": [
"info-circle"
],
"defaultCode": 61530,
"grid": 14
},
"attrs": [],
"properties": {
"id": 85,
"order": 3,
"name": "info-sign",
"prevSize": 28,
"code": 58882
},
"setIdx": 0,
"iconIdx": 2
},
{
"icon": {
"paths": [
"M733.714 419.429q0-16-10.286-26.286l-52-51.429q-10.857-10.857-25.714-10.857t-25.714 10.857l-233.143 232.571-129.143-129.143q-10.857-10.857-25.714-10.857t-25.714 10.857l-52 51.429q-10.286 10.286-10.286 26.286 0 15.429 10.286 25.714l206.857 206.857q10.857 10.857 25.714 10.857 15.429 0 26.286-10.857l310.286-310.286q10.286-10.286 10.286-25.714zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
],
"attrs": [],
"isMulticolor": false,
"tags": [
"check-circle"
],
"defaultCode": 61528,
"grid": 14
},
"attrs": [],
"properties": {
"id": 83,
"order": 9,
"prevSize": 28,
"code": 58886,
"name": "ok-sign"
},
"setIdx": 0,
"iconIdx": 6
},
{
"icon": {
"paths": [
"M658.286 475.429q0-105.714-75.143-180.857t-180.857-75.143-180.857 75.143-75.143 180.857 75.143 180.857 180.857 75.143 180.857-75.143 75.143-180.857zM950.857 950.857q0 29.714-21.714 51.429t-51.429 21.714q-30.857 0-51.429-21.714l-196-195.429q-102.286 70.857-228 70.857-81.714 0-156.286-31.714t-128.571-85.714-85.714-128.571-31.714-156.286 31.714-156.286 85.714-128.571 128.571-85.714 156.286-31.714 156.286 31.714 128.571 85.714 85.714 128.571 31.714 156.286q0 125.714-70.857 228l196 196q21.143 21.143 21.143 51.429z"
],
"width": 951,
"attrs": [],
"isMulticolor": false,
"tags": [
"search"
],
"defaultCode": 61442,
"grid": 14
},
"attrs": [],
"properties": {
"id": 2,
"order": 1,
"prevSize": 28,
"code": 58887,
"name": "icon-search"
},
"setIdx": 0,
"iconIdx": 7
}
],
"height": 1024,
"metadata": {
"name": "slate",
"license": "SIL OFL 1.1"
},
"preferences": {
"showGlyphs": true,
"showQuickUse": true,
"showQuickUse2": true,
"showSVGs": true,
"fontPref": {
"prefix": "icon-",
"metadata": {
"fontFamily": "slate",
"majorVersion": 1,
"minorVersion": 0,
"description": "Based on FontAwesome",
"license": "SIL OFL 1.1"
},
"metrics": {
"emSize": 1024,
"baseline": 6.25,
"whitespace": 50
},
"resetPoint": 58880,
"showSelector": false,
"selector": "class",
"classSelector": ".icon",
"showMetrics": false,
"showMetadata": true,
"showVersion": true,
"ie7": false
},
"imagePref": {
"prefix": "icon-",
"png": true,
"useClassSelector": true,
"color": 4473924,
"bgColor": 16777215
},
"historySize": 100,
"showCodes": true,
"gridSize": 16,
"showLiga": false
}
}

View File

@ -0,0 +1,95 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true
# this is based on https://github.com/rouge-ruby/rouge/blob/master/lib/rouge/themes/monokai_sublime.rb
# but without the added background, and changed styling for JSON keys to be soft_yellow instead of white
module Rouge
module Themes
class MonokaiSublimeSlate < CSSTheme
name 'monokai.sublime.slate'
palette :black => '#000000'
palette :bright_green => '#a6e22e'
palette :bright_pink => '#f92672'
palette :carmine => '#960050'
palette :dark => '#49483e'
palette :dark_grey => '#888888'
palette :dark_red => '#aa0000'
palette :dimgrey => '#75715e'
palette :emperor => '#555555'
palette :grey => '#999999'
palette :light_grey => '#aaaaaa'
palette :light_violet => '#ae81ff'
palette :soft_cyan => '#66d9ef'
palette :soft_yellow => '#e6db74'
palette :very_dark => '#1e0010'
palette :whitish => '#f8f8f2'
palette :orange => '#f6aa11'
palette :white => '#ffffff'
style Generic::Heading, :fg => :grey
style Literal::String::Regex, :fg => :orange
style Generic::Output, :fg => :dark_grey
style Generic::Prompt, :fg => :emperor
style Generic::Strong, :bold => false
style Generic::Subheading, :fg => :light_grey
style Name::Builtin, :fg => :orange
style Comment::Multiline,
Comment::Preproc,
Comment::Single,
Comment::Special,
Comment, :fg => :dimgrey
style Error,
Generic::Error,
Generic::Traceback, :fg => :carmine
style Generic::Deleted,
Generic::Inserted,
Generic::Emph, :fg => :dark
style Keyword::Constant,
Keyword::Declaration,
Keyword::Reserved,
Name::Constant,
Keyword::Type, :fg => :soft_cyan
style Literal::Number::Float,
Literal::Number::Hex,
Literal::Number::Integer::Long,
Literal::Number::Integer,
Literal::Number::Oct,
Literal::Number,
Literal::String::Char,
Literal::String::Escape,
Literal::String::Symbol, :fg => :light_violet
style Literal::String::Doc,
Literal::String::Double,
Literal::String::Backtick,
Literal::String::Heredoc,
Literal::String::Interpol,
Literal::String::Other,
Literal::String::Single,
Literal::String, :fg => :soft_yellow
style Name::Attribute,
Name::Class,
Name::Decorator,
Name::Exception,
Name::Function, :fg => :bright_green
style Name::Variable::Class,
Name::Namespace,
Name::Entity,
Name::Builtin::Pseudo,
Name::Variable::Global,
Name::Variable::Instance,
Name::Variable,
Text::Whitespace,
Text,
Name, :fg => :white
style Name::Label, :fg => :bright_pink
style Operator::Word,
Name::Tag,
Keyword,
Keyword::Namespace,
Keyword::Pseudo,
Operator, :fg => :bright_pink
end
end
end

View File

@ -0,0 +1,16 @@
module Multilang
def block_code(code, full_lang_name)
if full_lang_name
parts = full_lang_name.split('--')
rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified
super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match|
match + " tab-" + full_lang_name
end
else
super(code, full_lang_name)
end
end
end
require 'middleman-core/renderers/redcarpet'
Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang

View File

@ -0,0 +1,22 @@
# Nested unique header generation
require 'middleman-core/renderers/redcarpet'
class NestingUniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
def initialize
super
@@headers_history = {} if !defined?(@@headers_history)
end
def header(text, header_level)
friendly_text = text.gsub(/<[^>]*>/,"").parameterize
@@headers_history[header_level] = text.parameterize
if header_level > 1
for i in (header_level - 1).downto(1)
friendly_text.prepend("#{@@headers_history[i]}-") if @@headers_history.key?(i)
end
end
return "<h#{header_level} id='#{friendly_text}'>#{text}</h#{header_level}>"
end
end

View File

@ -0,0 +1,31 @@
require 'nokogiri'
def toc_data(page_content)
html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
# get a flat list of headers
headers = []
html_doc.css('h1, h2, h3').each do |header|
headers.push({
id: header.attribute('id').to_s,
content: header.children,
title: header.children.to_s.gsub(/<[^>]*>/, ''),
level: header.name[1].to_i,
children: []
})
end
[3,2].each do |header_level|
header_to_nest = nil
headers = headers.reject do |header|
if header[:level] == header_level
header_to_nest[:children].push header if header_to_nest
true
else
header_to_nest = header if header[:level] < header_level
false
end
end
end
headers
end

View File

@ -0,0 +1,24 @@
# Unique header generation
require 'middleman-core/renderers/redcarpet'
require 'digest'
class UniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
def initialize
super
@head_count = {}
end
def header(text, header_level)
friendly_text = text.gsub(/<[^>]*>/,"").parameterize
if friendly_text.strip.length == 0
# Looks like parameterize removed the whole thing! It removes many unicode
# characters like Chinese and Russian. To get a unique URL, let's just
# URI escape the whole header
friendly_text = Digest::SHA1.hexdigest(text)[0,10]
end
@head_count[friendly_text] ||= 0
@head_count[friendly_text] += 1
if @head_count[friendly_text] > 1
friendly_text += "-#{@head_count[friendly_text]}"
end
return "<h#{header_level} id='#{friendly_text}'>#{text}</h#{header_level}>"
end
end

248
app/docs/slate/slate.sh Executable file
View File

@ -0,0 +1,248 @@
#!/usr/bin/env bash
set -o errexit #abort if any command fails
me=$(basename "$0")
help_message="\
Usage: $me [<options>] <command> [<command-options>]
Run commands related to the slate process.
Commands:
serve Run the middleman server process, useful for
development.
build Run the build process.
deploy Will build and deploy files to branch. Use
--no-build to only deploy.
Global Options:
-h, --help Show this help information.
-v, --verbose Increase verbosity. Useful for debugging.
Deploy options:
-e, --allow-empty Allow deployment of an empty directory.
-m, --message MESSAGE Specify the message used when committing on the
deploy branch.
-n, --no-hash Don't append the source commit's hash to the deploy
commit's message.
--no-build Do not build the source files.
"
run_serve() {
exec bundle exec middleman serve --watcher-force-polling
}
run_build() {
bundle exec middleman build --clean
}
parse_args() {
# Set args from a local environment file.
if [ -e ".env" ]; then
source .env
fi
command=
# Parse arg flags
# If something is exposed as an environment variable, set/overwrite it
# here. Otherwise, set/overwrite the internal variable instead.
while : ; do
if [[ $1 = "-h" || $1 = "--help" ]]; then
echo "$help_message"
exit 0
elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
verbose=true
shift
elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
allow_empty=true
shift
elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
commit_message=$2
shift 2
elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
GIT_DEPLOY_APPEND_HASH=false
shift
elif [[ $1 = "--no-build" ]]; then
no_build=true
shift
elif [[ $1 = "serve" || $1 = "build" || $1 = "deploy" ]]; then
if [ ! -z "${command}" ]; then
>&2 echo "You can only specify one command."
exit 1
fi
command=$1
shift
elif [ -z $1 ]; then
break
fi
done
if [ -z "${command}" ]; then
>&2 echo "Command not specified."
exit 1
fi
# Set internal option vars from the environment and arg flags. All internal
# vars should be declared here, with sane defaults if applicable.
# Source directory & target branch.
deploy_directory=build
deploy_branch=gh-pages
#if no user identity is already set in the current git environment, use this:
default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
default_email=${GIT_DEPLOY_EMAIL:-}
#repository to deploy to. must be readable and writable.
repo=origin
#append commit hash to the end of message by default
append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
}
main() {
enable_expanded_output
if ! git diff --exit-code --quiet --cached; then
echo Aborting due to uncommitted changes in the index >&2
return 1
fi
commit_title=`git log -n 1 --format="%s" HEAD`
commit_hash=` git log -n 1 --format="%H" HEAD`
#default commit message uses last title if a custom one is not supplied
if [[ -z $commit_message ]]; then
commit_message="publish: $commit_title"
fi
#append hash to commit message unless no hash flag was found
if [ $append_hash = true ]; then
commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
fi
previous_branch=`git rev-parse --abbrev-ref HEAD`
if [ ! -d "$deploy_directory" ]; then
echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
return 1
fi
# must use short form of flag in ls for compatibility with macOS and BSD
if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
return 1
fi
if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
# deploy_branch exists in $repo; make sure we have the latest version
disable_expanded_output
git fetch --force $repo $deploy_branch:$deploy_branch
enable_expanded_output
fi
# check if deploy_branch exists locally
if git show-ref --verify --quiet "refs/heads/$deploy_branch"
then incremental_deploy
else initial_deploy
fi
restore_head
}
initial_deploy() {
git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
git --work-tree "$deploy_directory" add --all
commit+push
}
incremental_deploy() {
#make deploy_branch the current branch
git symbolic-ref HEAD refs/heads/$deploy_branch
#put the previously committed contents of deploy_branch into the index
git --work-tree "$deploy_directory" reset --mixed --quiet
git --work-tree "$deploy_directory" add --all
set +o errexit
diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
set -o errexit
case $diff in
0) echo No changes to files in $deploy_directory. Skipping commit.;;
1) commit+push;;
*)
echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to main, use: git symbolic-ref HEAD refs/heads/main && git reset --mixed >&2
return $diff
;;
esac
}
commit+push() {
set_user_id
git --work-tree "$deploy_directory" commit -m "$commit_message"
disable_expanded_output
#--quiet is important here to avoid outputting the repo URL, which may contain a secret token
git push --quiet $repo $deploy_branch
enable_expanded_output
}
#echo expanded commands as they are executed (for debugging)
enable_expanded_output() {
if [ $verbose ]; then
set -o xtrace
set +o verbose
fi
}
#this is used to avoid outputting the repo URL, which may contain a secret token
disable_expanded_output() {
if [ $verbose ]; then
set +o xtrace
set -o verbose
fi
}
set_user_id() {
if [[ -z `git config user.name` ]]; then
git config user.name "$default_username"
fi
if [[ -z `git config user.email` ]]; then
git config user.email "$default_email"
fi
}
restore_head() {
if [[ $previous_branch = "HEAD" ]]; then
#we weren't on any branch before, so just set HEAD back to the commit it was on
git update-ref --no-deref HEAD $commit_hash $deploy_branch
else
git symbolic-ref HEAD refs/heads/$previous_branch
fi
git reset --mixed
}
filter() {
sed -e "s|$repo|\$repo|g"
}
sanitize() {
"$@" 2> >(filter 1>&2) | filter
}
parse_args "$@"
if [ "${command}" = "serve" ]; then
run_serve
elif [[ "${command}" = "build" ]]; then
run_build
elif [[ ${command} = "deploy" ]]; then
if [[ ${no_build} != true ]]; then
run_build
fi
main "$@"
fi

Binary file not shown.

View File

@ -0,0 +1,14 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="slate" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe600;" d="M438.857 877.714q119.429 0 220.286-58.857t159.714-159.714 58.857-220.286-58.857-220.286-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857zM512 165.143v108.571q0 8-5.143 13.429t-12.571 5.429h-109.714q-7.429 0-13.143-5.714t-5.714-13.143v-108.571q0-7.429 5.714-13.143t13.143-5.714h109.714q7.429 0 12.571 5.429t5.143 13.429zM510.857 361.714l10.286 354.857q0 6.857-5.714 10.286-5.714 4.571-13.714 4.571h-125.714q-8 0-13.714-4.571-5.714-3.429-5.714-10.286l9.714-354.857q0-5.714 5.714-10t13.714-4.286h105.714q8 0 13.429 4.286t6 10z" />
<glyph unicode="&#xe602;" d="M585.143 164.571v91.429q0 8-5.143 13.143t-13.143 5.143h-54.857v292.571q0 8-5.143 13.143t-13.143 5.143h-182.857q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h54.857v-182.857h-54.857q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h256q8 0 13.143 5.143t5.143 13.143zM512 676.571v91.429q0 8-5.143 13.143t-13.143 5.143h-109.714q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h109.714q8 0 13.143 5.143t5.143 13.143zM877.714 438.857q0-119.429-58.857-220.286t-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857 220.286-58.857 159.714-159.714 58.857-220.286z" />
<glyph unicode="&#xe606;" d="M733.714 531.428q0 16-10.286 26.286l-52 51.429q-10.857 10.857-25.714 10.857t-25.714-10.857l-233.143-232.571-129.143 129.143q-10.857 10.857-25.714 10.857t-25.714-10.857l-52-51.429q-10.286-10.286-10.286-26.286 0-15.429 10.286-25.714l206.857-206.857q10.857-10.857 25.714-10.857 15.429 0 26.286 10.857l310.286 310.286q10.286 10.286 10.286 25.714zM877.714 438.857q0-119.429-58.857-220.286t-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857 220.286-58.857 159.714-159.714 58.857-220.286z" />
<glyph unicode="&#xe607;" d="M658.286 475.428q0 105.714-75.143 180.857t-180.857 75.143-180.857-75.143-75.143-180.857 75.143-180.857 180.857-75.143 180.857 75.143 75.143 180.857zM950.857 0q0-29.714-21.714-51.429t-51.429-21.714q-30.857 0-51.429 21.714l-196 195.429q-102.286-70.857-228-70.857-81.714 0-156.286 31.714t-128.571 85.714-85.714 128.571-31.714 156.286 31.714 156.286 85.714 128.571 128.571 85.714 156.286 31.714 156.286-31.714 128.571-85.714 85.714-128.571 31.714-156.286q0-125.714-70.857-228l196-196q21.143-21.143 21.143-51.429z" horiz-adv-x="951" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

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