Compare commits

...

152 Commits

Author SHA1 Message Date
jasder duan 2e56a43644 FIX 主页去掉‘开源镜像项目’ 2025-01-16 16:00:43 +08:00
jasder duan a71c834fda ADD 右上角登录按钮是否为sso单独登录按钮 2024-12-10 10:47:12 +08:00
jasder duan 1c6357b9ce FIX 主动同步头歌账号到gitea平台,防止前端进行弹框 2024-12-06 16:25:33 +08:00
jasder duan c7d99d14f8 FIX 主动同步头歌账号到gitea平台,防止前端进行弹框 2024-12-06 15:23:36 +08:00
jasder duan 5c3ebb9946 FIX 导航栏logo跳转链接问题 2023-11-21 16:26:38 +08:00
jasder duan 47c47f364d FIX 导航栏logo跳转链接问题 2023-11-21 16:21:30 +08:00
jasder duan aca5c9a162 FIX 激活流水线 激活问题 2023-11-17 22:03:08 +08:00
jasder duan c02b600547 FIX 激活流水线 repo信息不全问题 2023-11-17 21:44:22 +08:00
jasder duan 92b6b5de47 FIX 激活流水线创建webhook信息错误的问题 2023-11-17 20:36:52 +08:00
jasder duan 9a191847e4 FIX 激活流水线创建webhook信息错误的问题 2023-11-17 20:27:20 +08:00
jasder duan f7ce263d81 FIX 激活流水线创建webhook信息错误的问题 2023-11-17 20:21:07 +08:00
jasder duan bbc9f23eb5 ADD 日志 2023-11-17 20:10:36 +08:00
jasder duan 97cda03c23 FIX code review 2023-11-17 19:47:34 +08:00
jasder duan f2a073239c FIX 新建流水线创建ci repo没有生成临时秘钥问题 2023-11-17 16:40:18 +08:00
jasder duan c371b1c2e1 FIX 激活cicd时,没有绑定webhook的bug 2023-11-17 15:42:40 +08:00
jasder duan 3586f6cf20 FIX 添加日志 2023-11-16 19:47:02 +08:00
jasder duan e20102292d FIX 添加日志 2023-11-16 19:38:20 +08:00
jasder duan cead9e297b FIX 添加日志 2023-11-16 19:33:47 +08:00
xxq250 4135552311 fixed 项目分类统计数量实时统计,不统计私有 2023-09-20 17:22:11 +08:00
xxq250 d3e847e7dc fixed 项目分类统计数量实时统计,不统计私有 2023-09-20 17:08:16 +08:00
jasder duan 348bf5eb0f FIX 解决后台管理删除用户后,未删除对应项目,导致项目列表报错问题 2023-05-24 11:33:25 +08:00
“xxq250” 79ea003046 fixed 查询用户方法报错 2022-08-16 17:46:34 +08:00
xiaoxiaoqiong 7d46448121 fixed 同步gitea密码状态is_sync_pwd 2022-07-06 14:56:08 +08:00
xiaoxiaoqiong 3a16e7cf47 fixed 同步gitea密码状态 2022-07-06 14:37:58 +08:00
xiaoxiaoqiong f0798834e2 fix初始化统计数据 2022-05-11 11:27:25 +08:00
xiaoxiaoqiong 196a547fdd fix初始化统计数据 2022-05-11 10:06:13 +08:00
xiaoxiaoqiong cfc6e4f48a fix类型删除 2022-05-11 09:37:08 +08:00
xiaoxiaoqiong 435ce59320 重复建表 2022-04-15 15:58:39 +08:00
xiaoxiaoqiong 7c9a2505d9 去掉工程认证菜单 2022-04-14 18:13:10 +08:00
xiaoxiaoqiong d6b46dd093 log 2022-04-13 11:19:31 +08:00
xiaoxiaoqiong 746a5b091e log 2022-04-13 11:17:12 +08:00
xiaoxiaoqiong e3556a4aac log 2022-04-13 11:06:52 +08:00
xiaoxiaoqiong 12324d8c43 绑定email 2022-04-13 10:58:35 +08:00
xiaoxiaoqiong 63109147d1 屏蔽甘特图和质量分析 2022-04-13 10:17:59 +08:00
xiaoxiaoqiong 4de05dba70 db 2022-04-13 09:54:01 +08:00
xiaoxiaoqiong 30cd73406c db 2022-04-13 09:52:34 +08:00
xiaoxiaoqiong 1c22c8b5ea letter_avatar Gem 2022-04-13 09:47:28 +08:00
xiaoxiaoqiong 50f0c5cffa 删除多的文件 2022-04-13 09:42:30 +08:00
jasder 4494cbe3a9 fix 更新创建版本库条件 2021-07-16 10:24:27 +08:00
viletyy a1b18c07df Merge branch 'dev_educoder' into dev_edu_local 2021-07-14 11:49:33 +08:00
jasder 32ac61d889 FIX 更改默认链接 2021-07-13 16:05:26 +08:00
jasder 229885f42c ADD sonar[bug with percentage] 2021-07-13 15:53:05 +08:00
jasder 263636126b ADD sonar[bug] 2021-07-13 15:48:19 +08:00
jasder 35e11bf375 ADD sonar 2021-07-13 15:31:40 +08:00
jasder 69efc69e2d Merge branch 'dev_edu_local' of https://git.trustie.net/jasder/forgeplus into dev_edu_local 2021-07-13 10:12:28 +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
jasder 0bb45c7359 FIX 去掉密码修改功能权限控制 2021-07-08 17:47:11 +08:00
jasder 6aa7a3a3b2 ADD 工程认证导航栏 2021-07-08 17:46:37 +08:00
viletyy 7c0bc5d444 add: create project owner login 2021-07-07 15:51:05 +08:00
viletyy 334c0c2c09 add: create project owner login 2021-07-07 15:42:00 +08:00
jasder 113f8ded36 FIX [调整样式]去掉后台管理多余的功能 2021-07-07 14:44:16 +08:00
jasder 0178c8b913 FIX 去掉后台管理多余的功能 2021-07-07 14:40:33 +08:00
jasder dc07042e83 ADD sonar and gantt tab 2021-07-07 13:12:43 +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 01341d4389 FIX pull、 gitea pull model relation bug 2021-06-28 10:04:48 +08:00
jasder a62b29a263 Merge branch 'feature/pull_conflict_files' into dev_educoder 2021-06-28 10:00:10 +08:00
jasder 3a475a4674 FIX get conflict files bug with create pr api 2021-06-28 09:59:47 +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
141 changed files with 1155 additions and 22865 deletions

262
Gemfile
View File

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

View File

@ -6,6 +6,20 @@ class AccountsController < ApplicationController
render json: session
end
# 为了同步平台上未注册gitea的用户
def gitea_register
user = User.find_by(login: sync_gitea_params[:login])
Users::SyncGiteaForm.new(sync_gitea_params.merge(user: user)).validate!
return render_error("该用户已同步协作平台") if user.gitea_token.present? && user.gitea_uid.present?
result = create_gitea_user!(user, sync_gitea_params[:login], sync_gitea_params[:email], sync_gitea_params[:password])
result[:user] ? render_ok : render_error(result[:message])
rescue Exception => e
uid_logger_error(e.message)
tip_exception(-1, e.message)
end
# 其他平台同步注册的用户
def remote_register
username = params[:username]&.gsub(/\s+/, "")
@ -361,6 +375,10 @@ class AccountsController < ApplicationController
{ login: pre + code, email: email, phone: phone }
end
def sync_gitea_params
params.permit(:login, :email, :password)
end
def user_params
params.require(:user).permit(:login, :email, :phone)
end

View File

@ -43,7 +43,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController
end
def destroy
if @project_language.destroy
if @project_category.destroy
redirect_to admins_project_categories_path
flash[:success] = "删除成功"
else

View File

@ -16,6 +16,7 @@ class ApplicationController < ActionController::Base
before_action :check_sign
before_action :user_setup
#before_action :check_account
before_action :check_gitea_account
# TODO
# check sql query time
@ -278,6 +279,42 @@ class ApplicationController < ActionController::Base
end
end
# TODO
# 临时处理弹框同步gitea账号问题
def check_gitea_account
return unless current_user.logged?
if !current_user.has_gitea_user? || (current_user.has_gitea_user? && !current_user.is_sync_pwd)
begin
email = current_user.mail
if email.blank?
user_extension = current_user.user_extension
email_identifier =
if user_extension
if user_extension.identity == 'student'
current_user.student_id
elsif user_extension.identity == 'teacher'
current_user.staff_id
else
nil
end
else
nil
end
email = email_identifier.blank? ? "#{current_user.login}@qq.com" : "#{email_identifier}@qq.com"
current_user.update_column(:mail, email)
end
create_gitea_user!(current_user, current_user.login, email, 'zq123456')
rescue => e
Rails.logger.info "@@主动检测并同步gitea账号错误: #{e.message}"
return
end
end
end
# 系统全局认证(暂时隐藏试用申请的判断)
def check_auth
# day_cer = UserDayCertification.find_by(user_id: current_user.id)

View File

@ -45,8 +45,10 @@ class Ci::CloudAccountsController < Ci::BaseController
end
result = bind_hook!(current_user, @cloud_account, @repo)
Rails.logger.info "########## result: #{result}"
@project.update_columns(open_devops: true, gitea_webhook_id: result['id'])
@cloud_account.update_column(:ci_user_id, @ci_user.user_id)
Rails.logger.info "########## @cloud_account: #{@cloud_account.to_json} --- @repo: #{@repo.to_json} --- @project: #{@project}"
end
render_ok
rescue Exception => ex

View File

@ -40,18 +40,49 @@ class Ci::ProjectsController < Ci::BaseController
begin
ActiveRecord::Base.transaction do
if @repo
Rails.logger.info "==========> @repo已经存在 直接激活: #{@repo.to_json}"
Rails.logger.info "==========> @repo.repo_signer: #{@repo.repo_signer} ==> @repo.repo_secret: #{@repo.repo_secret}"
if @repo.repo_signer.blank? && @repo.repo_secret.blank?
@repo.update_columns(
repo_user_id: @ci_user.user_id,
repo_namespace: @project.owner.login,
repo_name: @project.identifier,
repo_slug: "#{@project.owner.login}/#{@project.identifier}",
repo_scm: "git",
repo_ssh_url: "",
repo_html_url: "",
repo_clone_url: @project.repository.url,
repo_active: 0,
repo_private: true,
repo_visibility: 'private',
repo_branch: 'master',
repo_counter: 0,
repo_trusted: false,
repo_protected: false,
repo_synced: 0,
repo_version: 1,
repo_timeout: 60,
repo_config: '.trustie-pipeline.yml',
repo_signer: Ci::Repo.generate_code!,
repo_secret: Ci::Repo.generate_code!
)
end
Rails.logger.info "==========> 更新后的repo: #{@repo.to_json}"
return render_error('该项目已经激活') if @repo.repo_active?
@repo.activate!(@project)
return render_ok
else
Rails.logger.info "==========> 没有@repo, 进行创建..."
@repo = Ci::Repo.auto_create!(@ci_user, @project)
@ci_user.update_column(:user_syncing, false)
end
result = bind_hook!(current_user, @cloud_account, @repo)
@project.update_columns(open_devops: true, gitea_webhook_id: result['id'])
@project.increment!(:open_devops_count)
@cloud_account.update_column(:ci_user_id, @ci_user.user_id)
if @project.gitea_webhook_id.blank?
result = bind_hook!(current_user, @cloud_account, @repo)
Rails.logger.info "########## result: #{result}"
@project.update_columns(open_devops: true, gitea_webhook_id: result['id'])
@project.increment!(:open_devops_count)
@cloud_account.update_column(:ci_user_id, @ci_user.user_id)
Rails.logger.info "########## @cloud_account: #{@cloud_account.to_json} --- @repo: #{@repo.to_json} --- @project: #{@project.to_json}"
end
end
render_ok
rescue Exception => ex

View File

@ -116,6 +116,9 @@ module Ci::CloudAccountManageable
},
type: "gitea"
}
Rails.logger.info "============> hook_params: #{hook_params}"
Rails.logger.info "============> user.gitea_token: #{user.gitea_token}"
result = Gitea::Hooks::CreateService.call(user.gitea_token, user.login, repo.repo_name, hook_params)
result[:status].present? ? nil : result

View File

@ -35,15 +35,16 @@ module LaboratoryHelper
# my_projects: "/users/#{current_user.try(:login)}/projects",
# my_projects: "https://www.trustie.net/users/#{current_user.try(:login)}/user_projectlist",
{
new_syllabuses: "https://www.trustie.net/syllabuses/new",
new_course: "https://www.trustie.net/courses/new",
edit_account: "https://www.trustie.net/my/account",
my_courses: "https://www.trustie.net/users/#{current_user.try(:login)}/user_courselist",
new_syllabuses: "https://forge.educoder.net/syllabuses/new",
new_course: "https://forge.educoder.net/courses/new",
edit_account: "https://forge.educoder.net/my/account",
my_courses: "https://forge.educoder.net/users/#{current_user.try(:login)}/user_courselist",
my_projects: "/users/#{current_user.try(:login)}/projects",
my_organ: "https://www.trustie.net/users/#{current_user.try(:login)}/user_organizations",
default_url: "https://www.trustie.net/",
tiding_url: "https://www.trustie.net/users/#{current_user.try(:login)}/user_messages",
register_url: "https://www.trustie.net/login?login=false"
my_organ: "https://forge.educoder.net/users/#{current_user.try(:login)}/user_organizations",
default_url: EduSetting.get("host_name"),
forge_url: "http://106.75.178.228:81",
tiding_url: "https://www.educoder.net/users/#{current_user.try(:login)}/user_tidings",
register_url: "https://www.educoder.net/user/register"
}
end
end

View File

@ -112,6 +112,7 @@ module LoginHelper
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params.merge(hash))
if interactor.success?
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea success _########"
user.update_column(:is_sync_pwd, true)
true
else
Rails.logger.info "########_ login is #{user.login} sync_pwd_to_gitea fail!: #{interactor.error}"

View File

@ -2,8 +2,6 @@ module RegisterHelper
extend ActiveSupport::Concern
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.password = password
user.platform = platform
@ -11,20 +9,36 @@ module RegisterHelper
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})
if interactor.success?
gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.call(username, password)
user.gitea_token = result['sha1']
user.gitea_uid = gitea_user[:body]['id']
if user.save!
UserExtension.create!(user_id: user.id)
result[:user] = {id: user.id, token: user.gitea_token}
if interactor.success? || interactor.error.to_s.include?("已被注册")
gitea_user = interactor.error.to_s.include?("已被注册") ? Gitea::User::GetService.call(username) : interactor.result
Rails.logger.info "##### [gitea] create_gitea_user result: #{gitea_user}"
if gitea_user[:body].present?
result = Gitea::User::GenerateTokenService.call(username, password)
forge_user.gitea_token = result['sha1']
forge_user.gitea_uid = gitea_user[:body]['id'] if gitea_user[:body].present? && gitea_user[:body]['id'] !=0
forge_user.mail = email
if forge_user.save
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
else
result[:message] = interactor.error
end
else
Rails.logger.info "##### [gitea] create_gitea_user result: #{interactor.error}"
result[:message] = interactor.error
end
result
end
def random_password
"#{Random.rand(11111111)}"
end
end

View File

@ -5,6 +5,14 @@ class ForksController < ApplicationController
def create
@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
private

View File

@ -21,6 +21,14 @@ class MainController < ApplicationController
uid_logger("main start is #{cookies[:_educoder_session]}")
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后再路由分发
if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild"
render file: 'public/h5educoderbuild/index.html', :layout => false

View File

@ -7,7 +7,16 @@ class MembersController < ApplicationController
before_action :check_member_not_exists!, only: %i[remove change_role]
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)
render_response(interactor)
rescue Exception => e
uid_logger_error(e.message)

View File

@ -20,6 +20,8 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources")
menu.append(menu_hash_by_name("activity"))
# menu.append(menu_hash_by_name("gantt"))
# menu.append(menu_hash_by_name("sonar"))
menu.append(menu_hash_by_name("setting")) if current_user.admin? || @project.manager?(current_user)
render json: menu
@ -47,6 +49,13 @@ class ProjectsController < ApplicationController
Projects::CreateForm.new(project_params).validate!
@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
rescue Exception => e
uid_logger_error(e.message)
@ -85,18 +94,13 @@ class ProjectsController < ApplicationController
end
def group_type_list
project_statics = ProjectStatistic.first
# project_statics = ProjectStatistic.first
@project_statics_list = [
{
project_type: 'common',
name: '开源托管项目',
projects_count: project_statics&.common_projects_count || 0
},
{
project_type: 'mirror',
name: '开源镜像项目',
projects_count: project_statics&.mirror_projects_count || 0
name: '所有项目',
projects_count: Project.common.visible.size || 0
}
]

View File

@ -56,7 +56,7 @@ class PullRequestsController < ApplicationController
ActiveRecord::Base.transaction do
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
if @gitea_pull_request[:status] == :success
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"])
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
render_ok
else
render_error("create pull request error: #{@gitea_pull_request[:status]}")

View File

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

View File

@ -4,7 +4,7 @@ class UsersController < ApplicationController
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 :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 :convert_image!, only: [:update]
skip_before_action :check_sign, only: [:attachment_show]
@ -254,23 +254,58 @@ class UsersController < ApplicationController
render_ok
end
def sync_user_info
# TODO: For Educoder
def change_password
user = User.find_by_login params[:login]
return render_forbidden unless user === current_user
form_params= {
login: params[:login],
email: user&.mail,
password: params[:password],
user: user
}
Gitea::User::ChangePasswordForm.new(form_params).validate!
sync_params = {
email: params[:email],
email: user&.mail,
password: params[:password]
}
Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate!
if sync_params.present?
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
if interactor.success?
user.update!(password: params[:password], is_sync_pwd: true)
render_ok
else
render_error(interactor.error)
end
end
end
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
if interactor.success?
user.update!(password: params[:password], mail: params[:email], status: User::STATUS_EDIT_INFO)
render_ok
else
render_error(interactor.error)
# TODO: For Educoder
def change_email
user = User.find_by_login params[:login]
form_params= {
login: params[:login],
email: user&.mail,
user: user
}
Gitea::User::ChangeEmailForm.new(form_params).validate!
sync_params = {
email: params[:email]
}
if sync_params.present?
interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
if interactor.success?
user.update!(mail: params[:email])
render_ok
else
render_error(interactor.error)
end
end
end

View File

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

View File

@ -1,18 +0,0 @@
# 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

View File

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

View File

@ -1,22 +0,0 @@
---
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

@ -1,5 +0,0 @@
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

@ -1,5 +0,0 @@
<!--
⚠️ 🚨 ⚠️ 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

@ -1,42 +0,0 @@
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

@ -1,41 +0,0 @@
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

@ -1,50 +0,0 @@
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

@ -1,22 +0,0 @@
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

View File

@ -1,27 +0,0 @@
*.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

View File

@ -1,254 +0,0 @@
# 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

@ -1,46 +0,0 @@
# 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/

View File

@ -1,12 +0,0 @@
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'

View File

@ -1,136 +0,0 @@
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

View File

@ -1,201 +0,0 @@
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.

View File

@ -1,81 +0,0 @@
<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.

View File

@ -1,46 +0,0 @@
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

View File

@ -1,65 +0,0 @@
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

View File

@ -1,226 +0,0 @@
#!/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

@ -1,148 +0,0 @@
{
"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

@ -1,95 +0,0 @@
# -*- 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

@ -1,16 +0,0 @@
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

@ -1,22 +0,0 @@
# 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

@ -1,31 +0,0 @@
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

@ -1,24 +0,0 @@
# 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

View File

@ -1,248 +0,0 @@
#!/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

View File

@ -1,36 +0,0 @@
---
title: Trustie API Reference
language_tabs: # must be one of https://git.io/vQNgJ
- shell: Shell
- javascript: JavaScript
toc_footers:
- <a href='https://www.trustie.net/login?login=false'>Sign Up for a User</a>
- <a href='https://www.trustie.net'>Powered by Trustie</a>
includes:
- licenses
- gitignores
- users
- projects
- repositories
- pulls
- issues
- organizations
- teams
- errors
search: true
code_clipboard: true
---
# Introduction
Welcome to the Trustie API! You can use our API to access Trustie API endpoints, which can get information on projects, repository, and users in our platform.
We have language bindings in Shell,avaScript! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.
This example API documentation page was created with [Trustie](https://www.trustie.net). Feel free to edit it and use it as a base for your own API's documentation.

View File

@ -1,14 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

View File

@ -1,39 +0,0 @@
# Authentication
> To authorize, use this code:
```ruby
require 'kittn'
api = Kittn::APIClient.authorize!('meowmeowmeow')
```
```python
import kittn
api = kittn.authorize('meowmeowmeow')
```
```shell
# With shell, you can just pass the correct header with each request
curl "api_endpoint_here" \
-H "Authorization: meowmeowmeow"
```
```javascript
const kittn = require('kittn');
let api = kittn.authorize('meowmeowmeow');
```
> Make sure to replace `meowmeowmeow` with your API key.
Kittn uses API keys to allow access to the API. You can register a new Kittn API key at our [developer portal](http://example.com/developers).
Kittn expects for the API key to be included in all API requests to the server in a header that looks like the following:
`Authorization: meowmeowmeow`
<aside class="notice">
You must replace <code>meowmeowmeow</code> with your personal API key.
</aside>

View File

@ -1,22 +0,0 @@
# Errors
<aside class="notice">
This error section is stored in a separate file in <code>includes/_errors.md</code>. Slate allows you to optionally separate out your docs into many files...just save them to the <code>includes</code> folder and add them to the top of your <code>index.md</code>'s frontmatter. Files are included in the order listed.
</aside>
The Kittn API uses the following error codes:
Error Code | Meaning
---------- | -------
400 | Bad Request -- Your request is invalid.
401 | Unauthorized -- Your API key is wrong.
403 | Forbidden -- The kitten requested is hidden for administrators only.
404 | Not Found -- The specified kitten could not be found.
405 | Method Not Allowed -- You tried to access a kitten with an invalid method.
406 | Not Acceptable -- You requested a format that isn't json.
410 | Gone -- The kitten requested has been removed from our servers.
418 | I'm a teapot.
429 | Too Many Requests -- You're requesting too many kittens! Slow down!
500 | Internal Server Error -- We had a problem with our server. Try again later.
503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

View File

@ -1,47 +0,0 @@
# Gitignores
## gitignore模板列表
获取gitignore模板列表, 支持名称搜索过滤
> 示例:
```shell
curl -X GET \
-d "name=Ada" \
http://localhost:3000/api/ignores.json
```
```javascript
await octokit.request('GET /api/ignores.json')
```
### HTTP 请求
`GET api/ignores.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
name |否| 否 ||string |gitignore名称
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
id |int |id |
name |string|gitignore名称|
> 返回的JSON示例:
```json
{
"ignores": [
{
"id": 1,
"name": "Ada"
}
]
}
```
<aside class="success">
Success — a happy kitten is an authenticated kitten!
</aside>

View File

@ -1 +0,0 @@
# Issues

View File

@ -1,57 +0,0 @@
# Licenses
## Get All Licenses
The Licenses API returns metadata about popular open source licenses and information about a particular project's license file.
> 示例:
```shell
curl -X GET \
-d "name=AFL" \
http://localhost:3000/api/licenses
```
```javascript
await octokit.request('GET /api/licenses')
```
### HTTP Request
`GET https://forgeplus.trustie.net/api/licenses.json`
### 请求参数
Name | Required | Type | Description
----- | ---- | ----- | -----
name |false|string |name of the license
> 返回字段说明:
```json
{
"licenses": [
{
"id": 57,
"name": "AFL-1.2"
},
{
"id": 76,
"name": "AFL-3.0"
},
{
"id": 214,
"name": "AFL-1.1"
},
{
"id": 326,
"name": "AFL-2.1"
},
{
"id": 350,
"name": "AFL-2.0"
}
]
}
```

View File

@ -1 +0,0 @@
# Organizations

View File

@ -1,852 +0,0 @@
# Projects
## 申请加入项目
申请加入项目
> 示例:
```shell
curl -X POST http://localhost:3000/api/applied_projects.json
```
```javascript
await octokit.request('POST /api/appliedr_projects.json')
```
### HTTP 请求
`POST /api/applied_projects.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|applied_project.code |是| |string |邀请码 |
|applied_project.role |否| |string |项目权限reporter: 报告者, developer: 开发者manager管理员 |
> 请求的JSON示例
```json
{
"applied_project": {
"code": "1una34",
"role": "developer"
}
}
```
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |申请id |
|status |string |申请状态canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
|time_ago |string |项目申请创建的时间 |
|project.id |int |申请项目的id |
|project.identifier |string |申请项目的标识 |
|project.name |string |申请项目的名称 |
|project.description |string |申请项目的描述 |
|project.is_public |bool |申请项目是否公开 |
|project.owner.id |bool |申请项目拥有者id |
|project.owner.type |string |申请项目拥有者类型 |
|project.owner.name |string |申请项目拥有者昵称 |
|project.owner.login |string |申请项目拥有者标识 |
|project.owner.image_url |string |申请项目拥有者头像 |
|user.id |int |申请创建者的id |
|user.type |string |申请创建者的类型 |
|user.name |string |申请创建者的名称 |
|user.login |string |申请创建者的标识 |
|user.image_url |string |申请创建者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 74,
"identifier": "hehuisssjssjjsjs",
"name": "hehuisssjssjjsjs",
"description": "wwww",
"is_public": false,
"owner": {
"id": 10,
"type": "User",
"name": "testforge1",
"login": "testforge1",
"image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
}
},
"user": {
"id": 6,
"type": "User",
"name": "何慧",
"login": "yystopf",
"image_url": "images/avatars/User/6?t=1622513134"
},
"id": 7,
"status": "common",
"created_at": "2021-06-09 16:41",
"time_ago": "1分钟前"
}
```
## 获取项目列表
获取项目列表,也可以更加相关条件过滤搜素
> 示例:
```shell
curl -X GET \
-d "page=1" \
-d "limit=5" \
http://localhost:3000/api/projects | jq
```
```javascript
await octokit.request('GET /api/projects')
```
### HTTP 请求
`GET api/projects`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
page | false | 1 | string | 页数,第几页
limit | false | 15 | string | 每页多少条数据默认15条
sort_by | false | | string | 排序类型, 取值updated_on、created_on、forked_count、praises_count; updated_on: 更新时间排序created_on: 创建时间排序forked_count: fork数据排序praises_count: 点赞数量排序默认为updated_on更新时间排序
sort_direction| false | | string | 排序方式,取值为: desc、asc; desc: 降序排序, asc: 升序排序, 默认为desc
search | false | | string | 按照项目名称搜索
category_id | false | | int | 项目类别id
language_id | false | | int | 项目语言id
project_type | false | | string | 项目类型, 取值为common、mirror; common:开源托管项目, mirror:开源镜像项目
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count | int | 项目总条数
id | string | 项目id
name | string | 项目名称
description | string | 项目简介
visits | int | 流量数
forked_count | int | 被fork的数量
praises_count | int | star数量
is_public | boolean | 是否公开, true:公开false:未公开
mirror_url | string | 镜像url
last_update_time| int | 最后更新时间为UNIX格式的时间戳
author | object | 项目创建者
-- name | string | 用户名,也是用户标识
category | object | 项目类别
-- id | int | 项目类型id
-- name | string | 项目类型名称
language | object | 项目语言
-- id | int | 项目语言id
-- name | string | 项目语言名称
> 返回的JSON示例:
```json
{
"total_count": 3096,
"projects": [
{
"id": 1400794,
"repo_id": 1402452,
"identifier": "cscw_2021_sponsor",
"name": "Sponsor机制下的开源贡献",
"description": "CSCW 2021 sponsor机制研究",
"visits": 5,
"praises_count": 0,
"forked_count": 0,
"is_public": true,
"mirror_url": null,
"type": 0,
"last_update_time": 1611971671,
"time_ago": "2天前",
"forked_from_project_id": null,
"open_devops": false,
"platform": "forge",
"author": {
"name": "张迅晖",
"login": "Nigel",
"image_url": "images/avatars/User/3675?t=1611832880"
},
"category": {
"id": 13,
"name": "云计算和大数据"
},
"language": {
"id": 34,
"name": "Python3.6"
}
}
]
}
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 推荐项目
获取推荐项目列表
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/projects/recommend | jq
```
```javascript
await octokit.request('GET /api/projects/recommend.json')
```
### HTTP 请求
`GET api/projects/recommend`
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count |int |项目总条数
id |string |项目id
name |string|项目名称
description |string|项目简介
visits |int|流量数
forked_count |int|被fork的数量
praises_count |int|star数量
is_public |boolean|是否公开, true:公开false:未公开
mirror_url |string|镜像url
last_update_time|int|最后更新时间为UNIX格式的时间戳
author |object|项目创建者
-- name |string|用户名,也是用户标识
category |object|项目类别
-- id |int|项目类型id
-- name |string|项目类型名称
language |object|项目语言
-- id |int|项目语言id
-- name |string|项目语言名称
> 返回的JSON示例:
```json
[
{
"id": 20,
"repo_id": 2,
"identifier": "PNAekinmH",
"name": "FNILL",
"visits": 13567,
"author": {
"name": "王一达",
"login": "wangyida",
"image_url": "avatars/User/b"
},
"category": {
"id": 8,
"name": "其他"
}
}
]
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目导航
获取项目导航信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/menu_list | jq
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/menu_list')
```
### HTTP 请求
`GET api/:owner/:repo/menu_list`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
menu_name |string|导航名称, home:主页,code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
> 返回的JSON示例:
```json
[
{
"menu_name": "home"
},
{
"menu_name": "code"
},
{
"menu_name": "pulls"
},
{
"menu_name": "activity"
}
]
```
## 项目主页
获取项目主页信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/jasder/forgeplus/about | jq
```
```javascript
await octokit.request('GET /api/jasder/forgeplus/about')
```
### HTTP 请求
`GET api/:owner/:repo/about`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
identifier |string|project's identifier
content |string|主页内容
attachments |array|附件
> 返回的JSON示例:
```json
{
"content": "",
"identifier": "forgeplus",
attachments: []
}
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目模块信息
项目模块信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`GET /api/yystopf/ceshi/project_units`
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|type |string|模块名称 |
> 返回的JSON示例:
```json
[
{
"type": "code"
},
{
"type": "pulls"
},
{
"type": "issues"
}
]
```
## 更改项目模块展示
更改项目模块展示
> 示例:
```shell
curl -X POST \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('POST /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`POST /api/yystopf/ceshi/project_units`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:易修,pulls:合并请求,devops:工作流,versions:里程碑 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 创建项目
创建项目
> 示例:
```shell
curl -X POST \
-d "user_id=36401" \
-d "name=hnfl_demo" \
-d "description=my first project" \
-d "repository_name=hnfl_demo" \
-d "project_category_id=1" \
-d "project_language_id=2" \
-d "ignore_id=2" \
-d "license_id=1" \
http://localhost:3000/api/projects.json
```
```javascript
await octokit.request('GET /api/projects.json')
```
### HTTP 请求
`POST api/projects`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|user_id |是| |int |用户id或者组织id |
|name |是| |string |项目名称 |
|description |是| |string |项目描述 |
|repository_name |是| |string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
|project_category_id|是| |int |项目类别id |
|project_language_id|是| |int |项目语言id |
|ignore_id |否| |int |gitignore相关id |
|license_id |否| |int |开源许可证id |
|private |否| |boolean|项目是否私有, true为私有false: 公开,默认为公开 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
> 返回的JSON示例:
```json
{
"id": 3240,
"name": "好项目"
}
```
## 创建镜像项目
创建镜像项目
> 示例:
```shell
curl -X POST \
-d "user_id=36408" \
-d "clone_addr=https://gitea.com/mx8090alex/golden.git" \
-d "name=golden_mirror1" \
-d "description=golden_mirror" \
-d "project_category_id=1" \
-d "project_language_id=2" \
http://localhost:3000/api/projects/migrate.json
```
```javascript
await octokit.request('GET /api/projects/migrate.json')
```
### HTTP 请求
`POST api/projects/migrate.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|user_id |是| |int |用户id或者组织id |
|name |是| |string |项目名称 |
|clone_addr |是| |string |镜像项目clone地址 |
|description |否| |string |项目描述 |
|repository_name |是| |string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
|project_category_id|是| |int |项目类别id |
|project_language_id|是| |int |项目语言id |
|is_mirror |否| |boolean|是否设置为镜像, true false默认为否 |
|auth_username |否| |string|镜像源仓库的登录用户名 |
|auth_password |否| |string|镜像源仓库的登录秘密 |
|private |否| |boolean|项目是否私有, true为私有false: 非私有,默认为公开 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
> 返回的JSON示例:
```json
{
"id": 3241,
"name": "这是一个镜像项目"
}
```
## 同步镜像
手动同步镜像
> 示例:
```shell
curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
```
```javascript
await octokit.request('POST /api/repositories/1244/sync_mirror.json')
```
### HTTP 请求
`POST api/repositories/:id/sync_mirror.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|id |是| |int |仓库id |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int |状态码, 0:标识请求成功 |
|message |string|服务端返回的信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## Fork项目
fork项目
> 示例:
```shell
curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
```
```javascript
await octokit.request('POST /api/jaser/jasder_test/forks.json')
```
### HTTP 请求
`POST api/:owner/:repo/forks.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |项目id |
|identifier |string|项目标识|
> 返回的JSON示例:
```json
{
"id": 3290,
"identifier": "newadm"
}
```
## 用户管理的组织列表
用户管理的组织列表
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json | jq
```
```javascript
await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
```
### HTTP 请求
`GET api/:owner/:repo/applied_transfer_projects/organizations`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
name |string|组织标识
nickname |string|组织名称
description|string|组织描述
avatar_url|string组织头像
> 返回的JSON示例:
```json
{
"total_count": 3,
"organizations": [
{
"id": 9,
"name": "ceshi_org",
"nickname": "测试组织",
"description": "测试组织",
"avatar_url": "images/avatars/Organization/9?t=1612706073"
},
{
"id": 51,
"name": "ceshi",
"nickname": "测试组织哈哈哈",
"description": "23212312",
"avatar_url": "images/avatars/Organization/51?t=1618800723"
},
{
"id": 52,
"name": "ceshi1",
"nickname": "身份卡手动阀",
"description": "1231手动阀是的",
"avatar_url": "images/avatars/Organization/52?t=1618805056"
}
]
}
```
## 迁移项目
迁移项目edit接口is_transfering为true表示正在迁移
> 示例:
```shell
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
```
```javascript
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
```
### HTTP 请求
`POST /api/:owner/:repo/applied_transfer_projects.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|owner_name|是| |string |迁移对象标识 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |项目id |
|status |string |项目迁移状态canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
|time_ago |string |项目迁移创建的时间 |
|project.id |int |迁移项目的id |
|project.identifier |string |迁移项目的标识 |
|project.name |string |迁移项目的名称 |
|project.description |string |迁移项目的描述 |
|project.is_public |bool |迁移项目是否公开 |
|project.owner.id |bool |迁移项目拥有者id |
|project.owner.type |string |迁移项目拥有者类型 |
|project.owner.name |string |迁移项目拥有者昵称 |
|project.owner.login |string |迁移项目拥有者标识 |
|project.owner.image_url |string |迁移项目拥有者头像 |
|user.id |int |迁移创建者的id |
|user.type |string |迁移创建者的类型 |
|user.name |string |迁移创建者的名称 |
|user.login |string |迁移创建者的标识 |
|user.image_url |string |迁移创建者头像 |
|owner.id |int |迁移接受者的id |
|owner.type |string |迁移接受者的类型 |
|owner.name |string |迁移接受者的名称 |
|owner.login |string |迁移接受者的标识 |
|owner.image_url |string |迁移接受者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 86,
"identifier": "ceshi_repo1",
"name": "测试项目啊1",
"description": "二十多",
"is_public": true,
"owner": {
"id": 52,
"type": "Organization",
"name": "身份卡手动阀",
"login": "ceshi1",
"image_url": "images/avatars/Organization/52?t=1618805056"
}
},
"user": {
"id": 6,
"type": "User",
"name": "yystopf",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
},
"owner": {
"id": 9,
"type": "Organization",
"name": "测试组织",
"login": "ceshi_org",
"image_url": "images/avatars/Organization/9?t=1612706073"
},
"id": 4,
"status": "common",
"created_at": "2021-04-26 09:54",
"time_ago": "1分钟前"
}
```
## 取消迁移项目
迁移项目edit接口is_transfering为true表示正在迁移
> 示例:
```shell
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
```
```javascript
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
```
### HTTP 请求
`POST /api/:owner/:repo/applied_transfer_projects/cancel.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |迁移id |
|status |string |迁移状态canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
|time_ago |string |迁移创建的时间 |
|project.id |int |迁移项目的id |
|project.identifier |string |迁移项目的标识 |
|project.name |string |迁移项目的名称 |
|project.description |string |迁移项目的描述 |
|project.is_public |bool |迁移项目是否公开 |
|project.owner.id |bool |迁移项目拥有者id |
|project.owner.type |string |迁移项目拥有者类型 |
|project.owner.name |string |迁移项目拥有者昵称 |
|project.owner.login |string |迁移项目拥有者标识 |
|project.owner.image_url |string |迁移项目拥有者头像 |
|user.id |int |迁移创建者的id |
|user.type |string |迁移创建者的类型 |
|user.name |string |迁移创建者的名称 |
|user.login |string |迁移创建者的标识 |
|user.image_url |string |迁移创建者头像 |
|owner.id |int |迁移接受者的id |
|owner.type |string |迁移接受者的类型 |
|owner.name |string |迁移接受者的名称 |
|owner.login |string |迁移接受者的标识 |
|owner.image_url |string |迁移接受者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 86,
"identifier": "ceshi_repo1",
"name": "测试项目啊1",
"description": "二十多",
"is_public": true,
"owner": {
"id": 52,
"type": "Organization",
"name": "身份卡手动阀",
"login": "ceshi1",
"image_url": "images/avatars/Organization/52?t=1618805056"
}
},
"user": {
"id": 6,
"type": "User",
"name": "yystopf",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
},
"owner": {
"id": 9,
"type": "Organization",
"name": "测试组织",
"login": "ceshi_org",
"image_url": "images/avatars/Organization/9?t=1612706073"
},
"id": 4,
"status": "common",
"created_at": "2021-04-26 09:54",
"time_ago": "1分钟前"
}
```

View File

@ -1 +0,0 @@
# Pulls

View File

@ -1,869 +0,0 @@
# Repositories
## 仓库详情
仓库详情
> 示例:
```shell
curl -X GET http://localhost:3000/api/jasder/jasder_test.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test.json')
```
### HTTP 请求
`GET /api/:owner/:repo`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
|identifier |string|项目标识|
|is_public |boolean|项目是否公开, true:公开false:私有|
|description |string|项目简介|
|repo_id |int|仓库id|
|repo_identifier|string|仓库标识|
> 返回的JSON示例:
```json
{
"name": "ni项目",
"identifier": "mirror_demo",
"is_public": true,
"description": "my first project mirror_demo",
"repo_id": 75073,
"repo_identifier": "mirror_demo"
}
```
## 仓库详情(简版)
仓库详情
> 示例:
```shell
curl -X GET http://localhost:3000/api/jasder/jasder_test/simple.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test/simple.json')
```
### HTTP 请求
`GET /api/:owner/:repo/simple`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
|identifier |string|项目标识|
|is_public |boolean|项目是否公开, true:公开false:私有|
|description |string|项目简介|
|repo_id |int|仓库id|
|repo_identifier|string|仓库标识|
> 返回的JSON示例:
```json
{
"name": "ni项目",
"identifier": "mirror_demo",
"is_public": true,
"description": "my first project mirror_demo",
"repo_id": 75073,
"repo_identifier": "mirror_demo"
}
```
## 仓库详情(新版)
仓库详情
> 示例:
```shell
curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/detail.json')
```
### HTTP 请求
`GET /api/:owner/:repo/detail`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|content |string |仓库简介 |
|website |string |仓库网址 |
|readme |string |readme文件
|identifier |string |项目标识 |
|name |string |项目名称 |
|issues_count |int |项目issue数量|
|pull_requests_count |int |项目合并请求数量|
|project_identifier |int |项目标识|
|praises_count |int |项目点赞数量|
|forked_count |int |项目复刻数量|
|watchers_count |int |项目关注数量|
|versions_count |int |项目里程碑数量|
|version_releases_count |int |项目发行版数量|
|version_releasesed_count |int |项目发行版已发行数量|
|permission |string |项目权限|
|mirror_url |string |镜像地址|
|mirror |bool |是否为镜像项目|
|type |int |项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目|
|open_devops |int |是否开启devops|
|watched |bool |是否关注|
|praised |bool |是否点赞|
|status |int |项目状态|
|forked_from_project_id |int |fork项目id|
|fork_info |object |fork项目信息|
|size |string |仓库大小|
|ssh_url |string |项目ssh地址|
|clone_url |string |项目克隆地址|
|default_branch |string |仓库默认分支|
|empty |bool |仓库是否为空|
|full_name |string |仓库全称|
|private |bool |仓库是否为私有项目|
|license_name |string |许可证名称|
|release_versions.list.name |string |项目issue数量|
|release_versions.list.tag_name |string |发行版标签名称|
|release_versions.list.created_at |string |发行版创建时间|
|release_versions.total_count |int |发行版数量|
|branches.list.name |string |分支名称|
|branches.total_count |int |分支数量|
|tags.list.name |string |标签名称|
|tags.total_count |int |标签数量|
|contributors.list.contributions|int |贡献数量|
|contributors.list.login |string |贡献者登录名|
|contributors.list.name |string |贡献者用户名称|
|contributors.list.image_url |string |贡献者头像|
|languages |object |项目语言占比|
> 返回的JSON示例:
```json
{
"content": "仓库简介",
"website": "仓库网址",
"readme": {
"type": "file",
"encoding": "base64",
"size": 9,
"name": "README.md",
"path": "README.md",
"content": "# ceshi\n\n",
"sha": ""
},
"identifier": "ceshi",
"name": "测试项目",
"project_id": 2,
"repo_id": 2,
"issues_count": 0,
"pull_requests_count": 0,
"project_identifier": "ceshi",
"praises_count": 0,
"forked_count": 0,
"watchers_count": 0,
"versions_count": 0,
"version_releases_count": 0,
"version_releasesed_count": 0,
"permission": "Reporter",
"mirror_url": null,
"mirror": false,
"type": 0,
"open_devops": false,
"watched": false,
"praised": false,
"status": 1,
"forked_from_project_id": 1,
"fork_info": {
"fork_form_name": "测试项目",
"fork_project_user_login": "ceshi_org",
"fork_project_identifier": "ceshi",
"fork_project_user_name": "ceshi_org"
},
"size": "25.0 KB",
"ssh_url": "virus@localhost:yystopf/ceshi.git",
"clone_url": "http://localhost:10080/yystopf/ceshi.git",
"default_branch": "master",
"empty": false,
"full_name": "yystopf/ceshi",
"private": false,
"license_name": "gnu-javamail-exception",
"release_versions": {
"list": [
{
"id": 2,
"name": "vvvv",
"tag_name": "v1.1",
"created_at": "2019-07-18 10:16"
}
],
"total_count": 1
},
"branches": {
"list": [
{
"name": "master"
}
],
"total_count": 1
},
"tags": {
"list": [
{
"name": "v1.1"
},
{
"name": "v1.0"
}
],
"total_count": 2
},
"contributors": {
"list": [
{
"contributions": 1,
"gid": 2,
"login": "yystopf",
"type": "User",
"name": "yystopf",
"image_url": "avatars/User/b"
}
],
"total_count": 1
},
"languages": {
"HTML": "50.9%",
"Ruby": "25.6%",
"JavaScript": "21.4%",
"CSS": "1.3%",
"CoffeeScript": "0.7%",
"Shell": "0.1%"
}
}
```
## 编辑仓库信息
编辑仓库信息
> 示例:
```shell
curl -X GET http://localhost:3000/api/jasder/jasder_test/edit.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test/edit.json')
```
### HTTP 请求
`GET /api/:owner/:repo/edit.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|identifier |string |仓库标识 |
|project_id |int|项目id|
|project_name |string|项目名称|
|project_identifier |string|项目标识|
|project_description |string|项目简介|
|project_category_id |int|项目类别id|
|project_language_id |int|项目语言id|
|private |boolean|项目是否私有, true为私有false: 公开 |
> 返回的JSON示例:
```json
{
"identifier": "project",
"project_id": 3263,
"project_name": "项目",
"project_identifier": "project identifier",
"project_description": "project description",
"project_category_id": 1,
"project_language_id": 2,
"private": false
}
```
## 修改仓库信息
修改仓库信息
> 示例:
```shell
curl -X PATCH \
-d "name=hnfl_demo" \
-d "description=my first project" \
-d "project_category_id=1" \
-d "project_language_id=2" \
-d "private=true" \
http://localhost:3000/api/jasder/jasder_test.json
```
```javascript
await octokit.request('PATCH /api/jasder/jasder_test.json')
```
### HTTP 请求
`PATCH /api/:owner/:repo`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|id |是 | |int |项目id |
|name |否 | |string |项目名称 |
|description |否 | |string |项目描述 |
|project_category_id|否 | |int |项目类别id |
|project_language_id|否 | |int |项目语言id |
|default_branch |否 | |string |默认分支名称 |
|private |否 | |boolean|项目是否私有, true为私有false: 公开,默认为公开 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int|id |
|identifier |string|项目标识|
|name |string|项目名称|
|description |string|项目简介|
|project_category_id|int|项目类别id|
|project_language_id|int|项目语言id|
|private |否|boolean|项目是否私有, true为私有false: 公开,默认为公开 |
> 返回的JSON示例:
```json
{
"id": 3263,
"identifier": "project identifier",
"name": "project name",
"description": "project description",
"project_category_id": 1,
"project_language_id": 2,
"is_public": true
}
```
## 删除仓库
删除仓库
> 示例:
```shell
curl -X DELETE http://localhost:3000/api/jasder/jasder_test.json
```
```javascript
await octokit.request('DELETE /api/jasder/jasder_test.json')
```
### HTTP 请求
`PATCH /api/:owner/:repo`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="warning">
只有平台管理员和项目管理员才能删除仓库.
</aside>
## 添加仓库成员
仓库中添加成员操作
> 示例:
```shell
curl -X POST \
-d "user_id=12" \
http://localhost:3000/api/jasder/jasder_test/collaborators.json
```
```javascript
await octokit.request('POST /api/jasder/jasder_test/collaborators.json')
```
### HTTP 请求
`POST /api/:owner/:repo/collaborators.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|user_id |是|int | |用户id |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="warning">
只有平台管理员和项目管理员才能添加仓库成员.
</aside>
## 删除仓库成员
仓库中删除成员操作
> 示例:
```shell
curl -X DELETE \
-d "user_id=12" \
http://localhost:3000/api/jasder/jasder_test/collaborators.json
```
```javascript
await octokit.request('DELETE /api/jasder/jasder_test/collaborators.json')
```
### HTTP 请求
`DELETE /api/:owner/:repo/collaborators.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|user_id |是|int | |用户id |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="warning">
只有平台管理员和项目管理员才能删除仓库成员.
</aside>
## 更改仓库成员角色(权限)
更改仓库成员角色
> 示例:
```shell
curl -X PUT \
-d "user_id=12" \
-d "role=Developer" \
http://localhost:3000/api/jasder/jasder_test/change_role.json
```
```javascript
await octokit.request('PUT /api/jasder/jasder_test/change_role.json')
```
### HTTP 请求
`PUT /api/:owner/:repo/change_role.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|user_id |是|int | |用户id |
|role |是|string | |取值范围:"Manager", "Developer", "Reporter";分别为项目管理人员(拥有所有操作权限)、项目开发人员(只拥有读写权限)、项目报告人员(只拥有读权限) |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="warning">
只有平台管理员和项目管理员才能更改仓库成员角色.
</aside>
## 获取仓库成员列表
获取仓库成员列表
> 示例:
```shell
curl -X GET \
-d "page=1" \
-d "limit=5" \
http://localhost:3000/api/jasder/jasder_test/collaborators.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test/collaborators.json')
```
### HTTP 请求
`GET /api/:owner/:repo/collaborators.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|page |否| |string |页数,第几页 |
|limit |否| |string |每页多少条数据默认15条 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_count |int |返回记录总条数 |
|members |array|项目成员信息|
|-- id |int|用户id|
|-- name |string|用户名称|
|-- login |string|用户登录名/标识|
|-- image_url |string|用户头像|
|-- is_owner |boolean|是否是项目的拥有者true:是, false:不是|
|-- role |string|该用户在项目中的角色, Manager: 管理员(拥有操作权限); Developer:开发人员(只拥有读写权限) Reporter:报告人员(只拥有读权限)|
> 返回的JSON示例:
```json
{
"total_count": 2,
"members": [
{
"id": 36401,
"name": "name",
"login": "login",
"image_url": "avatars/User/b",
"is_owner": true,
"role": "Manager"
},
{
"id": 36399,
"name": "name",
"login": "login",
"image_url": "avatars/User/b",
"is_owner": false,
"role": "Developer"
}
]
}
```
<aside class="success">
Success Data.
</aside>
## 获取仓库所有文件
获取仓库所有文件
> 示例:
```shell
curl -X GET \
-d "ref=develop" \
http://localhost:3000/api/yystopf/ceshi/files.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/files.json')
```
### HTTP 请求
`GET /api/:owner/:repo/files`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|search |否| |string |文件搜索关键词 |
|ref |是| |string |分支名,默认为仓库默认分支 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|name |string |文件名称|
|path |string |文件路径|
|sha |string |文件标识|
|type |string |文件类型|
|size |int |文件大小|
|url |string |文件地址|
> 返回的JSON示例:
```json
[
{
"name": ".gitignore",
"path": ".gitignore",
"sha": "f83922d01ae60f6e637a1a2b9f08871b4f87dfc8",
"type": "file",
"size": 63,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/.gitignore?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/.gitignore"
},
{
"name": "LICENSE",
"path": "LICENSE",
"sha": "8f3b9ab0d08afd3a624d822e3971a2f42b3bc2b9",
"type": "file",
"size": 341,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/LICENSE?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/LICENSE"
},
{
"name": "README.md",
"path": "README.md",
"sha": "1bc8a60ac6ddc876ebc4b60fc68991435bfad93e",
"type": "file",
"size": 9,
"url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/README.md?ref=master",
"html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/README.md"
}
]
```
## 获取仓库代码目录
获取仓库代码目录
> 示例:
```shell
curl -X GET \
-d "ref=develop" \
http://localhost:3000//api/jasder/jasder_test/entries.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test/entries.json')
```
### HTTP 请求
`GET /api/:owner/:repo/entries.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|ref |否| | string |分支名称、tag名称或是提交记录id默认为master分支 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|last_commit |object |
|-- commit |object |
|id |int |id |
|name |string|文件夹或文件名称|
|path |string|文件夹或文件相对路径|
|type |string|文件类型, file:文件dir文件目录
|size |int|文件夹或文件大小 单位B
|content |string|文件内容
|target |string|标签
> 返回的JSON示例:
```json
{
"last_commit": {
"commit": {
"sha": "3f2de4f78d2d7050486535082cd11cdfc9f3679e",
"url": "http://localhost:3003//api/repositories/api-cloud-platform/commits/3f2de4f78d2d7050486535082cd11cdfc9f3679e",
"message": "update README.md.",
"author": {
"name": "Gitee",
"email": "noreply@gitee.com",
"date": "2020-03-02T20:23:18+08:00"
},
"committer": {
"name": "Gitee",
"email": "noreply@gitee.com",
"date": "2020-03-02T20:23:18+08:00"
},
"timestamp": 1583151798,
"time_from_now": "3个月前"
},
"author": null,
"committer": null
},
"entries": [
{
"name": "ace-gate",
"path": "ace-gate",
"sha": "c83f85fc63b14edcd6fc502eee9996f5a9993eca",
"type": "dir",
"size": 0,
"content": null,
"target": null,
"commit": {
"message": "v2.9 升级alibaba组件release版本\n",
"sha": "6117eaab86f71115f42f2a46ff1683015cda798d",
"created_at": "1970-01-01 08:00",
"time_from_now": "1年前",
"created_at_unix": null
}
}
]
}
```
<aside class="success">
Success Data.
</aside>
## 获取仓库代码子目录或者文件
获取仓库代码子目录或者文件
> 示例:
```shell
curl -X GET \
-d "ref=master" \
-d "filepath=file" \
http://localhost:3000//api/jasder/jasder_test/sub_entries.json
```
```javascript
await octokit.request('GET /api/jasder/jasder_test/sub_entries.json')
```
### HTTP 请求
`GET /api/:owner/:repo/sub_entries.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|filepath |是|string |文件夹、文件的相对路径 |
|ref |否| | string |分支名称、tag名称或是提交记录id默认为master分支 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|文件夹或文件名称|
|path |string|文件夹或文件相对路径|
|type |string|文件类型, file:文件dir文件目录|
|size |int|文件夹或文件大小 单位KB|
|content |string|文件内容,|
|target |string|标签|
|url |string|文件访问链接,带分支|
|html_url |string|文件访问链接,未标识分支|
|git_url |string|文件夹或文件的git仓库访问链接|
|download_url |string|文件下载、文件内容访问链接|
> 返回的JSON示例:
```json
[
{
"name": "build.rc",
"path": "lib/build.rc",
"type": "",
"size": 1268,
"content": null,
"target": null,
"url": "http://localhost:3003/api/v1/repos/18816895620/mirror_demo/contents/lib/build.rc?ref=master",
"html_url": "http://localhost:3003/18816895620/mirror_demo/src/branch/master/lib/build.rc",
"git_url": "http://localhost:3003/api/v1/repos/18816895620/mirror_demo/git/blobs/191fcf1a63b3777e2977fcede7dd5309efdd70fe",
"download_url": null
}
]
```
<aside class="success">
Success Data.
</aside>

View File

@ -1 +0,0 @@
# Teams

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
//= require ./all_nosearch
//= require ./app/_search

View File

@ -1,27 +0,0 @@
//= require ./lib/_energize
//= require ./app/_copy
//= require ./app/_toc
//= require ./app/_lang
function adjustLanguageSelectorWidth() {
const elem = $('.dark-box > .lang-selector');
elem.width(elem.parent().width());
}
$(function() {
loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10);
setupLanguages($('body').data('languages'));
$('.content').imagesLoaded( function() {
window.recacheHeights();
window.refreshToc();
});
$(window).resize(function() {
adjustLanguageSelectorWidth();
});
adjustLanguageSelectorWidth();
});
window.onpopstate = function() {
activateLanguage(getLanguageFromQueryString());
};

View File

@ -1,15 +0,0 @@
function copyToClipboard(container) {
const el = document.createElement('textarea');
el.value = container.textContent.replace(/\n$/, '');
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
function setupCodeCopy() {
$('pre.highlight').prepend('<div class="copy-clipboard"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Copy to Clipboard</title><path d="M18 6v-6h-18v18h6v6h18v-18h-6zm-12 10h-4v-14h14v4h-10v10zm16 6h-14v-14h14v14z"></path></svg></div>');
$('.copy-clipboard').on('click', function() {
copyToClipboard(this.parentNode.children[1]);
});
}

View File

@ -1,164 +0,0 @@
//= require ../lib/_jquery
/*
Copyright 2008-2013 Concur Technologies, Inc.
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.
*/
;(function () {
'use strict';
var languages = [];
window.setupLanguages = setupLanguages;
window.activateLanguage = activateLanguage;
window.getLanguageFromQueryString = getLanguageFromQueryString;
function activateLanguage(language) {
if (!language) return;
if (language === "") return;
$(".lang-selector a").removeClass('active');
$(".lang-selector a[data-language-name='" + language + "']").addClass('active');
for (var i=0; i < languages.length; i++) {
$(".highlight.tab-" + languages[i]).hide();
$(".lang-specific." + languages[i]).hide();
}
$(".highlight.tab-" + language).show();
$(".lang-specific." + language).show();
window.recacheHeights();
// scroll to the new location of the position
if ($(window.location.hash).get(0)) {
$(window.location.hash).get(0).scrollIntoView(true);
}
}
// parseURL and stringifyURL are from https://github.com/sindresorhus/query-string
// MIT licensed
// https://github.com/sindresorhus/query-string/blob/7bee64c16f2da1a326579e96977b9227bf6da9e6/license
function parseURL(str) {
if (typeof str !== 'string') {
return {};
}
str = str.trim().replace(/^(\?|#|&)/, '');
if (!str) {
return {};
}
return str.split('&').reduce(function (ret, param) {
var parts = param.replace(/\+/g, ' ').split('=');
var key = parts[0];
var val = parts[1];
key = decodeURIComponent(key);
// missing `=` should be `null`:
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
val = val === undefined ? null : decodeURIComponent(val);
if (!ret.hasOwnProperty(key)) {
ret[key] = val;
} else if (Array.isArray(ret[key])) {
ret[key].push(val);
} else {
ret[key] = [ret[key], val];
}
return ret;
}, {});
};
function stringifyURL(obj) {
return obj ? Object.keys(obj).sort().map(function (key) {
var val = obj[key];
if (Array.isArray(val)) {
return val.sort().map(function (val2) {
return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
}).join('&');
}
return encodeURIComponent(key) + '=' + encodeURIComponent(val);
}).join('&') : '';
};
// gets the language set in the query string
function getLanguageFromQueryString() {
if (location.search.length >= 1) {
var language = parseURL(location.search).language;
if (language) {
return language;
} else if (jQuery.inArray(location.search.substr(1), languages) != -1) {
return location.search.substr(1);
}
}
return false;
}
// returns a new query string with the new language in it
function generateNewQueryString(language) {
var url = parseURL(location.search);
if (url.language) {
url.language = language;
return stringifyURL(url);
}
return language;
}
// if a button is clicked, add the state to the history
function pushURL(language) {
if (!history) { return; }
var hash = window.location.hash;
if (hash) {
hash = hash.replace(/^#+/, '');
}
history.pushState({}, '', '?' + generateNewQueryString(language) + '#' + hash);
// save language as next default
localStorage.setItem("language", language);
}
function setupLanguages(l) {
var defaultLanguage = localStorage.getItem("language");
languages = l;
var presetLanguage = getLanguageFromQueryString();
if (presetLanguage) {
// the language is in the URL, so use that language!
activateLanguage(presetLanguage);
localStorage.setItem("language", presetLanguage);
} else if ((defaultLanguage !== null) && (jQuery.inArray(defaultLanguage, languages) != -1)) {
// the language was the last selected one saved in localstorage, so use that language!
activateLanguage(defaultLanguage);
} else {
// no language selected, so use the default
activateLanguage(languages[0]);
}
}
// if we click on a language tab, activate that language
$(function() {
$(".lang-selector a").on("click", function() {
var language = $(this).data("language-name");
pushURL(language);
activateLanguage(language);
return false;
});
});
})();

View File

@ -1,102 +0,0 @@
//= require ../lib/_lunr
//= require ../lib/_jquery
//= require ../lib/_jquery.highlight
;(function () {
'use strict';
var content, searchResults;
var highlightOpts = { element: 'span', className: 'search-highlight' };
var searchDelay = 0;
var timeoutHandle = 0;
var index;
function populate() {
index = lunr(function(){
this.ref('id');
this.field('title', { boost: 10 });
this.field('body');
this.pipeline.add(lunr.trimmer, lunr.stopWordFilter);
var lunrConfig = this;
$('h1, h2').each(function() {
var title = $(this);
var body = title.nextUntil('h1, h2');
lunrConfig.add({
id: title.prop('id'),
title: title.text(),
body: body.text()
});
});
});
determineSearchDelay();
}
$(populate);
$(bind);
function determineSearchDelay() {
if (index.tokenSet.toArray().length>5000) {
searchDelay = 300;
}
}
function bind() {
content = $('.content');
searchResults = $('.search-results');
$('#input-search').on('keyup',function(e) {
var wait = function() {
return function(executingFunction, waitTime){
clearTimeout(timeoutHandle);
timeoutHandle = setTimeout(executingFunction, waitTime);
};
}();
wait(function(){
search(e);
}, searchDelay);
});
}
function search(event) {
var searchInput = $('#input-search')[0];
unhighlight();
searchResults.addClass('visible');
// ESC clears the field
if (event.keyCode === 27) searchInput.value = '';
if (searchInput.value) {
var results = index.search(searchInput.value).filter(function(r) {
return r.score > 0.0001;
});
if (results.length) {
searchResults.empty();
$.each(results, function (index, result) {
var elem = document.getElementById(result.ref);
searchResults.append("<li><a href='#" + result.ref + "'>" + $(elem).text() + "</a></li>");
});
highlight.call(searchInput);
} else {
searchResults.html('<li></li>');
$('.search-results li').text('No Results Found for "' + searchInput.value + '"');
}
} else {
unhighlight();
searchResults.removeClass('visible');
}
}
function highlight() {
if (this.value) content.highlight(this.value, highlightOpts);
}
function unhighlight() {
content.unhighlight(highlightOpts);
}
})();

View File

@ -1,122 +0,0 @@
//= require ../lib/_jquery
//= require ../lib/_imagesloaded.min
;(function () {
'use strict';
var htmlPattern = /<[^>]*>/g;
var loaded = false;
var debounce = function(func, waitTime) {
var timeout = false;
return function() {
if (timeout === false) {
setTimeout(function() {
func();
timeout = false;
}, waitTime);
timeout = true;
}
};
};
var closeToc = function() {
$(".toc-wrapper").removeClass('open');
$("#nav-button").removeClass('open');
};
function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) {
var headerHeights = {};
var pageHeight = 0;
var windowHeight = 0;
var originalTitle = document.title;
var recacheHeights = function() {
headerHeights = {};
pageHeight = $(document).height();
windowHeight = $(window).height();
$toc.find(tocLinkSelector).each(function() {
var targetId = $(this).attr('href');
if (targetId[0] === "#") {
headerHeights[targetId] = $("#" + $.escapeSelector(targetId.substring(1))).offset().top;
}
});
};
var refreshToc = function() {
var currentTop = $(document).scrollTop() + scrollOffset;
if (currentTop + windowHeight >= pageHeight) {
// at bottom of page, so just select last header by making currentTop very large
// this fixes the problem where the last header won't ever show as active if its content
// is shorter than the window height
currentTop = pageHeight + 1000;
}
var best = null;
for (var name in headerHeights) {
if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) {
best = name;
}
}
// Catch the initial load case
if (currentTop == scrollOffset && !loaded) {
best = window.location.hash;
loaded = true;
}
var $best = $toc.find("[href='" + best + "']").first();
if (!$best.hasClass("active")) {
// .active is applied to the ToC link we're currently on, and its parent <ul>s selected by tocListSelector
// .active-expanded is applied to the ToC links that are parents of this one
$toc.find(".active").removeClass("active");
$toc.find(".active-parent").removeClass("active-parent");
$best.addClass("active");
$best.parents(tocListSelector).addClass("active").siblings(tocLinkSelector).addClass('active-parent');
$best.siblings(tocListSelector).addClass("active");
$toc.find(tocListSelector).filter(":not(.active)").slideUp(150);
$toc.find(tocListSelector).filter(".active").slideDown(150);
if (window.history.replaceState) {
window.history.replaceState(null, "", best);
}
var thisTitle = $best.data("title");
if (thisTitle !== undefined && thisTitle.length > 0) {
document.title = thisTitle.replace(htmlPattern, "") + " " + originalTitle;
} else {
document.title = originalTitle;
}
}
};
var makeToc = function() {
recacheHeights();
refreshToc();
$("#nav-button").click(function() {
$(".toc-wrapper").toggleClass('open');
$("#nav-button").toggleClass('open');
return false;
});
$(".page-wrapper").click(closeToc);
$(".toc-link").click(closeToc);
// reload immediately after scrolling on toc click
$toc.find(tocLinkSelector).click(function() {
setTimeout(function() {
refreshToc();
}, 0);
});
$(window).scroll(debounce(refreshToc, 200));
$(window).resize(debounce(recacheHeights, 200));
};
makeToc();
window.recacheHeights = recacheHeights;
window.refreshToc = refreshToc;
}
window.loadToc = loadToc;
})();

View File

@ -1,169 +0,0 @@
/**
* energize.js v0.1.0
*
* Speeds up click events on mobile devices.
* https://github.com/davidcalhoun/energize.js
*/
(function() { // Sandbox
/**
* Don't add to non-touch devices, which don't need to be sped up
*/
if(!('ontouchstart' in window)) return;
var lastClick = {},
isThresholdReached, touchstart, touchmove, touchend,
click, closest;
/**
* isThresholdReached
*
* Compare touchstart with touchend xy coordinates,
* and only fire simulated click event if the coordinates
* are nearby. (don't want clicking to be confused with a swipe)
*/
isThresholdReached = function(startXY, xy) {
return Math.abs(startXY[0] - xy[0]) > 5 || Math.abs(startXY[1] - xy[1]) > 5;
};
/**
* touchstart
*
* Save xy coordinates when the user starts touching the screen
*/
touchstart = function(e) {
this.startXY = [e.touches[0].clientX, e.touches[0].clientY];
this.threshold = false;
};
/**
* touchmove
*
* Check if the user is scrolling past the threshold.
* Have to check here because touchend will not always fire
* on some tested devices (Kindle Fire?)
*/
touchmove = function(e) {
// NOOP if the threshold has already been reached
if(this.threshold) return false;
this.threshold = isThresholdReached(this.startXY, [e.touches[0].clientX, e.touches[0].clientY]);
};
/**
* touchend
*
* If the user didn't scroll past the threshold between
* touchstart and touchend, fire a simulated click.
*
* (This will fire before a native click)
*/
touchend = function(e) {
// Don't fire a click if the user scrolled past the threshold
if(this.threshold || isThresholdReached(this.startXY, [e.changedTouches[0].clientX, e.changedTouches[0].clientY])) {
return;
}
/**
* Create and fire a click event on the target element
* https://developer.mozilla.org/en/DOM/event.initMouseEvent
*/
var touch = e.changedTouches[0],
evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
evt.simulated = true; // distinguish from a normal (nonsimulated) click
e.target.dispatchEvent(evt);
};
/**
* click
*
* Because we've already fired a click event in touchend,
* we need to listed for all native click events here
* and suppress them as necessary.
*/
click = function(e) {
/**
* Prevent ghost clicks by only allowing clicks we created
* in the click event we fired (look for e.simulated)
*/
var time = Date.now(),
timeDiff = time - lastClick.time,
x = e.clientX,
y = e.clientY,
xyDiff = [Math.abs(lastClick.x - x), Math.abs(lastClick.y - y)],
target = closest(e.target, 'A') || e.target, // needed for standalone apps
nodeName = target.nodeName,
isLink = nodeName === 'A',
standAlone = window.navigator.standalone && isLink && e.target.getAttribute("href");
lastClick.time = time;
lastClick.x = x;
lastClick.y = y;
/**
* Unfortunately Android sometimes fires click events without touch events (seen on Kindle Fire),
* so we have to add more logic to determine the time of the last click. Not perfect...
*
* Older, simpler check: if((!e.simulated) || standAlone)
*/
if((!e.simulated && (timeDiff < 500 || (timeDiff < 1500 && xyDiff[0] < 50 && xyDiff[1] < 50))) || standAlone) {
e.preventDefault();
e.stopPropagation();
if(!standAlone) return false;
}
/**
* Special logic for standalone web apps
* See http://stackoverflow.com/questions/2898740/iphone-safari-web-app-opens-links-in-new-window
*/
if(standAlone) {
window.location = target.getAttribute("href");
}
/**
* Add an energize-focus class to the targeted link (mimics :focus behavior)
* TODO: test and/or remove? Does this work?
*/
if(!target || !target.classList) return;
target.classList.add("energize-focus");
window.setTimeout(function(){
target.classList.remove("energize-focus");
}, 150);
};
/**
* closest
* @param {HTMLElement} node current node to start searching from.
* @param {string} tagName the (uppercase) name of the tag you're looking for.
*
* Find the closest ancestor tag of a given node.
*
* Starts at node and goes up the DOM tree looking for a
* matching nodeName, continuing until hitting document.body
*/
closest = function(node, tagName){
var curNode = node;
while(curNode !== document.body) { // go up the dom until we find the tag we're after
if(!curNode || curNode.nodeName === tagName) { return curNode; } // found
curNode = curNode.parentNode; // not found, so keep going up
}
return null; // not found
};
/**
* Add all delegated event listeners
*
* All the events we care about bubble up to document,
* so we can take advantage of event delegation.
*
* Note: no need to wait for DOMContentLoaded here
*/
document.addEventListener('touchstart', touchstart, false);
document.addEventListener('touchmove', touchmove, false);
document.addEventListener('touchend', touchend, false);
document.addEventListener('click', click, true); // TODO: why does this use capture?
})();

File diff suppressed because one or more lines are too long

View File

@ -1,108 +0,0 @@
/*
* jQuery Highlight plugin
*
* Based on highlight v3 by Johann Burkard
* http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
*
* Code a little bit refactored and cleaned (in my humble opinion).
* Most important changes:
* - has an option to highlight only entire words (wordsOnly - false by default),
* - has an option to be case sensitive (caseSensitive - false by default)
* - highlight element tag and class names can be specified in options
*
* Usage:
* // wrap every occurrance of text 'lorem' in content
* // with <span class='highlight'> (default options)
* $('#content').highlight('lorem');
*
* // search for and highlight more terms at once
* // so you can save some time on traversing DOM
* $('#content').highlight(['lorem', 'ipsum']);
* $('#content').highlight('lorem ipsum');
*
* // search only for entire word 'lorem'
* $('#content').highlight('lorem', { wordsOnly: true });
*
* // don't ignore case during search of term 'lorem'
* $('#content').highlight('lorem', { caseSensitive: true });
*
* // wrap every occurrance of term 'ipsum' in content
* // with <em class='important'>
* $('#content').highlight('ipsum', { element: 'em', className: 'important' });
*
* // remove default highlight
* $('#content').unhighlight();
*
* // remove custom highlight
* $('#content').unhighlight({ element: 'em', className: 'important' });
*
*
* Copyright (c) 2009 Bartek Szopka
*
* Licensed under MIT license.
*
*/
jQuery.extend({
highlight: function (node, re, nodeName, className) {
if (node.nodeType === 3) {
var match = node.data.match(re);
if (match) {
var highlight = document.createElement(nodeName || 'span');
highlight.className = className || 'highlight';
var wordNode = node.splitText(match.index);
wordNode.splitText(match[0].length);
var wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
return 1; //skip added node in parent
}
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
for (var i = 0; i < node.childNodes.length; i++) {
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
}
}
return 0;
}
});
jQuery.fn.unhighlight = function (options) {
var settings = { className: 'highlight', element: 'span' };
jQuery.extend(settings, options);
return this.find(settings.element + "." + settings.className).each(function () {
var parent = this.parentNode;
parent.replaceChild(this.firstChild, this);
parent.normalize();
}).end();
};
jQuery.fn.highlight = function (words, options) {
var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
jQuery.extend(settings, options);
if (words.constructor === String) {
words = [words];
}
words = jQuery.grep(words, function(word, i){
return word != '';
});
words = jQuery.map(words, function(word, i) {
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
});
if (words.length == 0) { return this; };
var flag = settings.caseSensitive ? "" : "i";
var pattern = "(" + words.join("|") + ")";
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);
return this.each(function () {
jQuery.highlight(this, re, settings.element, settings.className);
});
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,128 +0,0 @@
<%#
Copyright 2008-2013 Concur Technologies, Inc.
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.
%>
<% language_tabs = current_page.data.language_tabs || [] %>
<% page_content = yield %>
<%
if current_page.data.includes
current_page.data.includes.each do |include|
page_content += partial("includes/#{include}")
end
end
%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><%= current_page.data.title || "API Documentation" %></title>
<style media="screen">
<%= Rouge::Themes::MonokaiSublimeSlate.render(:scope => '.highlight') %>
</style>
<style media="print">
* {
transition:none!important;
}
<%= Rouge::Themes::Base16::Solarized.render(:scope => '.highlight') %>
</style>
<%= stylesheet_link_tag :screen, media: :screen %>
<%= stylesheet_link_tag :print, media: :print %>
<% if current_page.data.search %>
<%= javascript_include_tag "all" %>
<% else %>
<%= javascript_include_tag "all_nosearch" %>
<% end %>
<% if current_page.data.code_clipboard %>
<script>
$(function() { setupCodeCopy(); });
</script>
<% end %>
</head>
<body class="<%= page_classes %>" data-languages="<%=h language_tabs.map{ |lang| lang.is_a?(Hash) ? lang.keys.first : lang }.to_json %>">
<a href="#" id="nav-button">
<span>
NAV
<%= image_tag('navbar.png') %>
</span>
</a>
<div class="toc-wrapper">
<%= image_tag "logo.png", class: 'logo' %>
<% if language_tabs.any? %>
<div class="lang-selector">
<% language_tabs.each do |lang| %>
<% if lang.is_a? Hash %>
<a href="#" data-language-name="<%= lang.keys.first %>"><%= lang.values.first %></a>
<% else %>
<a href="#" data-language-name="<%= lang %>"><%= lang %></a>
<% end %>
<% end %>
</div>
<% end %>
<% if current_page.data.search %>
<div class="search">
<input type="text" class="search" id="input-search" placeholder="Search">
</div>
<ul class="search-results"></ul>
<% end %>
<ul id="toc" class="toc-list-h1">
<% toc_data(page_content).each do |h1| %>
<li>
<a href="#<%= h1[:id] %>" class="toc-h1 toc-link" data-title="<%= h1[:title] %>"><%= h1[:content] %></a>
<% if h1[:children].length > 0 %>
<ul class="toc-list-h2">
<% h1[:children].each do |h2| %>
<li>
<a href="#<%= h2[:id] %>" class="toc-h2 toc-link" data-title="<%= h2[:title] %>"><%= h2[:content] %></a>
</li>
<% end %>
</ul>
<% end %>
</li>
<% end %>
</ul>
<% if current_page.data.toc_footers %>
<ul class="toc-footer">
<% current_page.data.toc_footers.each do |footer| %>
<li><%= footer %></li>
<% end %>
</ul>
<% end %>
</div>
<div class="page-wrapper">
<div class="dark-box"></div>
<div class="content">
<%= page_content %>
</div>
<div class="dark-box">
<% if language_tabs.any? %>
<div class="lang-selector">
<% language_tabs.each do |lang| %>
<% if lang.is_a? Hash %>
<a href="#" data-language-name="<%= lang.keys.first %>"><%= lang.values.first %></a>
<% else %>
<a href="#" data-language-name="<%= lang %>"><%= lang %></a>
<% end %>
<% end %>
</div>
<% end %>
</div>
</div>
</body>
</html>

View File

@ -1,38 +0,0 @@
@font-face {
font-family: 'slate';
src:font-url('slate.eot?-syv14m');
src:font-url('slate.eot?#iefix-syv14m') format('embedded-opentype'),
font-url('slate.woff2?-syv14m') format('woff2'),
font-url('slate.woff?-syv14m') format('woff'),
font-url('slate.ttf?-syv14m') format('truetype'),
font-url('slate.svg?-syv14m#slate') format('svg');
font-weight: normal;
font-style: normal;
}
%icon {
font-family: 'slate';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
}
%icon-exclamation-sign {
@extend %icon;
content: "\e600";
}
%icon-info-sign {
@extend %icon;
content: "\e602";
}
%icon-ok-sign {
@extend %icon;
content: "\e606";
}
%icon-search {
@extend %icon;
content: "\e607";
}

View File

@ -1,427 +0,0 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

View File

@ -1,140 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// RTL Styles Variables
////////////////////////////////////////////////////////////////////////////////
$default: auto;
////////////////////////////////////////////////////////////////////////////////
// TABLE OF CONTENTS
////////////////////////////////////////////////////////////////////////////////
#toc>ul>li>a>span {
float: left;
}
.toc-wrapper {
transition: right 0.3s ease-in-out !important;
left: $default !important;
#{right}: 0;
}
.toc-h2 {
padding-#{right}: $nav-padding + $nav-indent;
}
#nav-button {
#{right}: 0;
transition: right 0.3s ease-in-out;
&.open {
right: $nav-width
}
}
////////////////////////////////////////////////////////////////////////////////
// PAGE LAYOUT AND CODE SAMPLE BACKGROUND
////////////////////////////////////////////////////////////////////////////////
.page-wrapper {
margin-#{left}: $default !important;
margin-#{right}: $nav-width;
.dark-box {
#{right}: $default;
#{left}: 0;
}
}
.lang-selector {
width: $default !important;
a {
float: right;
}
}
////////////////////////////////////////////////////////////////////////////////
// CODE SAMPLE STYLES
////////////////////////////////////////////////////////////////////////////////
.content {
&>h1,
&>h2,
&>h3,
&>h4,
&>h5,
&>h6,
&>p,
&>table,
&>ul,
&>ol,
&>aside,
&>dl {
margin-#{left}: $examples-width;
margin-#{right}: $default !important;
}
&>ul,
&>ol {
padding-#{right}: $main-padding + 15px;
}
table {
th,
td {
text-align: right;
}
}
dd {
margin-#{right}: 15px;
}
aside {
aside:before {
padding-#{left}: 0.5em;
}
.search-highlight {
background: linear-gradient(to top right, #F7E633 0%, #F1D32F 100%);
}
}
pre,
blockquote {
float: left !important;
clear: left !important;
}
}
////////////////////////////////////////////////////////////////////////////////
// TYPOGRAPHY
////////////////////////////////////////////////////////////////////////////////
h1,
h2,
h3,
h4,
h5,
h6,
p,
aside {
text-align: right;
direction: rtl;
}
.toc-wrapper {
text-align: right;
direction: rtl;
font-weight: 100 !important;
}
////////////////////////////////////////////////////////////////////////////////
// RESPONSIVE DESIGN
////////////////////////////////////////////////////////////////////////////////
@media (max-width: $tablet-width) {
.toc-wrapper {
#{right}: -$nav-width;
&.open {
#{right}: 0;
}
}
.page-wrapper {
margin-#{right}: 0;
}
}
@media (max-width: $phone-width) {
%left-col {
margin-#{left}: 0;
}
}

View File

@ -1,103 +0,0 @@
/*
Copyright 2008-2013 Concur Technologies, Inc.
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.
*/
////////////////////////////////////////////////////////////////////////////////
// CUSTOMIZE SLATE
////////////////////////////////////////////////////////////////////////////////
// Use these settings to help adjust the appearance of Slate
// BACKGROUND COLORS
////////////////////
$nav-bg: #2E3336 !default;
$examples-bg: #2E3336 !default;
$code-bg: #1E2224 !default;
$code-annotation-bg: #191D1F !default;
$nav-subitem-bg: #1E2224 !default;
$nav-active-bg: #0F75D4 !default;
$nav-active-parent-bg: #1E2224 !default; // parent links of the current section
$lang-select-border: #000 !default;
$lang-select-bg: #1E2224 !default;
$lang-select-active-bg: $examples-bg !default; // feel free to change this to blue or something
$lang-select-pressed-bg: #111 !default; // color of language tab bg when mouse is pressed
$main-bg: #F3F7F9 !default;
$aside-notice-bg: #8fbcd4 !default;
$aside-warning-bg: #c97a7e !default;
$aside-success-bg: #6ac174 !default;
$search-notice-bg: #c97a7e !default;
// TEXT COLORS
////////////////////
$main-text: #333 !default; // main content text color
$nav-text: #fff !default;
$nav-active-text: #fff !default;
$nav-active-parent-text: #fff !default; // parent links of the current section
$lang-select-text: #fff !default; // color of unselected language tab text
$lang-select-active-text: #fff !default; // color of selected language tab text
$lang-select-pressed-text: #fff !default; // color of language tab text when mouse is pressed
// SIZES
////////////////////
$nav-width: 230px !default; // width of the navbar
$examples-width: 50% !default; // portion of the screen taken up by code examples
$logo-margin: 0px !default; // margin below logo
$main-padding: 28px !default; // padding to left and right of content & examples
$nav-padding: 15px !default; // padding to left and right of navbar
$nav-v-padding: 10px !default; // padding used vertically around search boxes and results
$nav-indent: 10px !default; // extra padding for ToC subitems
$code-annotation-padding: 13px !default; // padding inside code annotations
$h1-margin-bottom: 21px !default; // padding under the largest header tags
$tablet-width: 930px !default; // min width before reverting to tablet size
$phone-width: $tablet-width - $nav-width !default; // min width before reverting to mobile size
// FONTS
////////////////////
%default-font {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 14px;
}
%header-font {
@extend %default-font;
font-weight: bold;
}
%code-font {
font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;
font-size: 12px;
line-height: 1.5;
}
// OTHER
////////////////////
$nav-footer-border-color: #666 !default;
$search-box-border-color: #666 !default;
////////////////////////////////////////////////////////////////////////////////
// INTERNAL
////////////////////////////////////////////////////////////////////////////////
// These settings are probably best left alone.
%break-words {
word-break: break-all;
hyphens: auto;
}

View File

@ -1,153 +0,0 @@
@charset "utf-8";
@import 'normalize';
@import 'variables';
@import 'icon-font';
/*
Copyright 2008-2013 Concur Technologies, Inc.
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.
*/
$print-color: #999;
$print-color-light: #ccc;
$print-font-size: 12px;
body {
@extend %default-font;
}
.tocify, .toc-footer, .lang-selector, .search, #nav-button {
display: none;
}
.tocify-wrapper>img {
margin: 0 auto;
display: block;
}
.content {
font-size: 12px;
pre, code {
@extend %code-font;
@extend %break-words;
border: 1px solid $print-color;
border-radius: 5px;
font-size: 0.8em;
}
pre {
code {
border: 0;
}
}
pre {
padding: 1.3em;
}
code {
padding: 0.2em;
}
table {
border: 1px solid $print-color;
tr {
border-bottom: 1px solid $print-color;
}
td,th {
padding: 0.7em;
}
}
p {
line-height: 1.5;
}
a {
text-decoration: none;
color: #000;
}
h1 {
@extend %header-font;
font-size: 2.5em;
padding-top: 0.5em;
padding-bottom: 0.5em;
margin-top: 1em;
margin-bottom: $h1-margin-bottom;
border: 2px solid $print-color-light;
border-width: 2px 0;
text-align: center;
}
h2 {
@extend %header-font;
font-size: 1.8em;
margin-top: 2em;
border-top: 2px solid $print-color-light;
padding-top: 0.8em;
}
h1+h2, h1+div+h2 {
border-top: none;
padding-top: 0;
margin-top: 0;
}
h3, h4 {
@extend %header-font;
font-size: 0.8em;
margin-top: 1.5em;
margin-bottom: 0.8em;
text-transform: uppercase;
}
h5, h6 {
text-transform: uppercase;
}
aside {
padding: 1em;
border: 1px solid $print-color-light;
border-radius: 5px;
margin-top: 1.5em;
margin-bottom: 1.5em;
line-height: 1.6;
}
aside:before {
vertical-align: middle;
padding-right: 0.5em;
font-size: 14px;
}
aside.notice:before {
@extend %icon-info-sign;
}
aside.warning:before {
@extend %icon-exclamation-sign;
}
aside.success:before {
@extend %icon-ok-sign;
}
}
.copy-clipboard {
@media print {
display: none
}
}

View File

@ -1,634 +0,0 @@
@charset "utf-8";
@import 'normalize';
@import 'variables';
@import 'icon-font';
// @import 'rtl'; // uncomment to switch to RTL format
/*
Copyright 2008-2013 Concur Technologies, Inc.
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.
*/
////////////////////////////////////////////////////////////////////////////////
// GENERAL STUFF
////////////////////////////////////////////////////////////////////////////////
html, body {
color: $main-text;
padding: 0;
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@extend %default-font;
background-color: $main-bg;
height: 100%;
-webkit-text-size-adjust: none; /* Never autoresize text */
}
////////////////////////////////////////////////////////////////////////////////
// TABLE OF CONTENTS
////////////////////////////////////////////////////////////////////////////////
#toc > ul > li > a > span {
float: right;
background-color: #2484FF;
border-radius: 40px;
width: 20px;
}
.toc-wrapper {
transition: left 0.3s ease-in-out;
overflow-y: auto;
overflow-x: hidden;
position: fixed;
z-index: 30;
top: 0;
left: 0;
bottom: 0;
width: $nav-width;
background-color: $nav-bg;
font-size: 13px;
font-weight: bold;
// language selector for mobile devices
.lang-selector {
display: none;
a {
padding-top: 0.5em;
padding-bottom: 0.5em;
}
}
// This is the logo at the top of the ToC
.logo {
display: block;
margin: 15px auto;
max-width: 100%;
margin-bottom: $logo-margin;
}
&>.search {
position: relative;
input {
background: $nav-bg;
border-width: 0 0 1px 0;
border-color: $search-box-border-color;
padding: 6px 0 6px 20px;
box-sizing: border-box;
margin: $nav-v-padding $nav-padding;
width: $nav-width - ($nav-padding*2);
outline: none;
color: $nav-text;
border-radius: 0; /* ios has a default border radius */
}
&:before {
position: absolute;
top: 17px;
left: $nav-padding;
color: $nav-text;
@extend %icon-search;
}
}
.search-results {
margin-top: 0;
box-sizing: border-box;
height: 0;
overflow-y: auto;
overflow-x: hidden;
transition-property: height, margin;
transition-duration: 180ms;
transition-timing-function: ease-in-out;
background: $nav-subitem-bg;
&.visible {
height: 30%;
margin-bottom: 1em;
}
li {
margin: 1em $nav-padding;
line-height: 1;
}
a {
color: $nav-text;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
// The Table of Contents is composed of multiple nested
// unordered lists. These styles remove the default
// styling of an unordered list because it is ugly.
ul, li {
list-style: none;
margin: 0;
padding: 0;
line-height: 28px;
}
li {
color: $nav-text;
transition-property: background;
transition-timing-function: linear;
transition-duration: 200ms;
}
// This is the currently selected ToC entry
.toc-link.active {
background-color: $nav-active-bg;
color: $nav-active-text;
}
// this is parent links of the currently selected ToC entry
.toc-link.active-parent {
background-color: $nav-active-parent-bg;
color: $nav-active-parent-text;
}
.toc-list-h2 {
display: none;
background-color: $nav-subitem-bg;
font-weight: 500;
}
.toc-h2 {
padding-left: $nav-padding + $nav-indent;
font-size: 12px;
}
.toc-footer {
padding: 1em 0;
margin-top: 1em;
border-top: 1px dashed $nav-footer-border-color;
li,a {
color: $nav-text;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
li {
font-size: 0.8em;
line-height: 1.7;
text-decoration: none;
}
}
}
.toc-link, .toc-footer li {
padding: 0 $nav-padding 0 $nav-padding;
display: block;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-decoration: none;
color: $nav-text;
transition-property: background;
transition-timing-function: linear;
transition-duration: 130ms;
}
// button to show navigation on mobile devices
#nav-button {
span {
display: block;
$side-pad: $main-padding / 2 - 8px;
padding: $side-pad $side-pad $side-pad;
background-color: rgba($main-bg, 0.7);
transform-origin: 0 0;
transform: rotate(-90deg) translate(-100%, 0);
border-radius: 0 0 0 5px;
}
padding: 0 1.5em 5em 0; // increase touch size area
display: none;
position: fixed;
top: 0;
left: 0;
z-index: 100;
color: #000;
text-decoration: none;
font-weight: bold;
opacity: 0.7;
line-height: 16px;
img {
height: 16px;
vertical-align: bottom;
}
transition: left 0.3s ease-in-out;
&:hover { opacity: 1; }
&.open {left: $nav-width}
}
////////////////////////////////////////////////////////////////////////////////
// PAGE LAYOUT AND CODE SAMPLE BACKGROUND
////////////////////////////////////////////////////////////////////////////////
.page-wrapper {
margin-left: $nav-width;
position: relative;
z-index: 10;
background-color: $main-bg;
min-height: 100%;
padding-bottom: 1px; // prevent margin overflow
// The dark box is what gives the code samples their dark background.
// It sits essentially under the actual content block, which has a
// transparent background.
// I know, it's hackish, but it's the simplist way to make the left
// half of the content always this background color.
.dark-box {
width: $examples-width;
background-color: $examples-bg;
position: absolute;
right: 0;
top: 0;
bottom: 0;
}
.lang-selector {
position: fixed;
z-index: 50;
border-bottom: 5px solid $lang-select-active-bg;
}
}
.lang-selector {
display: flex;
background-color: $lang-select-bg;
width: 100%;
font-weight: bold;
overflow-x: auto;
a {
display: inline;
color: $lang-select-text;
text-decoration: none;
padding: 0 10px;
line-height: 30px;
outline: 0;
&:active, &:focus {
background-color: $lang-select-pressed-bg;
color: $lang-select-pressed-text;
}
&.active {
background-color: $lang-select-active-bg;
color: $lang-select-active-text;
}
}
&:after {
content: '';
clear: both;
display: block;
}
}
////////////////////////////////////////////////////////////////////////////////
// CONTENT STYLES
////////////////////////////////////////////////////////////////////////////////
// This is all the stuff with the light background in the left half of the page
.content {
// fixes webkit rendering bug for some: see #538
-webkit-transform: translateZ(0);
// to place content above the dark box
position: relative;
z-index: 30;
&:after {
content: '';
display: block;
clear: both;
}
&>h1, &>h2, &>h3, &>h4, &>h5, &>h6, &>p, &>table, &>ul, &>ol, &>aside, &>dl {
margin-right: $examples-width;
padding: 0 $main-padding;
box-sizing: border-box;
display: block;
@extend %left-col;
}
&>ul, &>ol {
padding-left: $main-padding + 15px;
}
// the div is the tocify hidden div for placeholding stuff
&>h1, &>h2, &>div {
clear:both;
}
h1 {
@extend %header-font;
font-size: 25px;
padding-top: 0.5em;
padding-bottom: 0.5em;
margin-bottom: $h1-margin-bottom;
margin-top: 2em;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
background-color: #fdfdfd;
}
h1:first-child, div:first-child + h1 {
border-top-width: 0;
margin-top: 0;
}
h2 {
@extend %header-font;
font-size: 19px;
margin-top: 4em;
margin-bottom: 0;
border-top: 1px solid #ccc;
padding-top: 1.2em;
padding-bottom: 1.2em;
background-image: linear-gradient(to bottom, rgba(#fff, 0.2), rgba(#fff, 0));
}
// h2s right after h1s should bump right up
// against the h1s.
h1 + h2, h1 + div + h2 {
margin-top: $h1-margin-bottom * -1;
border-top: none;
}
h3, h4, h5, h6 {
@extend %header-font;
font-size: 15px;
margin-top: 2.5em;
margin-bottom: 0.8em;
}
h4, h5, h6 {
font-size: 10px;
}
hr {
margin: 2em 0;
border-top: 2px solid $examples-bg;
border-bottom: 2px solid $main-bg;
}
table {
margin-bottom: 1em;
overflow: auto;
th,td {
text-align: left;
vertical-align: top;
line-height: 1.6;
code {
white-space: nowrap;
}
}
th {
padding: 5px 10px;
border-bottom: 1px solid #ccc;
vertical-align: bottom;
}
td {
padding: 10px;
}
tr:last-child {
border-bottom: 1px solid #ccc;
}
tr:nth-child(odd)>td {
background-color: lighten($main-bg,4.2%);
}
tr:nth-child(even)>td {
background-color: lighten($main-bg,2.4%);
}
}
dt {
font-weight: bold;
}
dd {
margin-left: 15px;
}
p, li, dt, dd {
line-height: 1.6;
margin-top: 0;
}
img {
max-width: 100%;
}
code {
background-color: rgba(0,0,0,0.05);
padding: 3px;
border-radius: 3px;
@extend %break-words;
@extend %code-font;
}
pre>code {
background-color: transparent;
padding: 0;
}
aside {
padding-top: 1em;
padding-bottom: 1em;
margin-top: 1.5em;
margin-bottom: 1.5em;
background: $aside-notice-bg;
line-height: 1.6;
&.warning {
background-color: $aside-warning-bg;
}
&.success {
background-color: $aside-success-bg;
}
}
aside:before {
vertical-align: middle;
padding-right: 0.5em;
font-size: 14px;
}
aside.notice:before {
@extend %icon-info-sign;
}
aside.warning:before {
@extend %icon-exclamation-sign;
}
aside.success:before {
@extend %icon-ok-sign;
}
.search-highlight {
padding: 2px;
margin: -3px;
border-radius: 4px;
border: 1px solid #F7E633;
background: linear-gradient(to top left, #F7E633 0%, #F1D32F 100%);
}
}
////////////////////////////////////////////////////////////////////////////////
// CODE SAMPLE STYLES
////////////////////////////////////////////////////////////////////////////////
// This is all the stuff that appears in the right half of the page
.content {
&>div.highlight {
clear:none;
}
pre, blockquote {
background-color: $code-bg;
color: #fff;
margin: 0;
width: $examples-width;
float:right;
clear:right;
box-sizing: border-box;
@extend %right-col;
&>p { margin: 0; }
a {
color: #fff;
text-decoration: none;
border-bottom: dashed 1px #ccc;
}
}
pre {
@extend %code-font;
padding-top: 2em;
padding-bottom: 2em;
padding: 2em $main-padding;
}
blockquote {
&>p {
background-color: $code-annotation-bg;
padding: $code-annotation-padding 2em;
color: #eee;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// RESPONSIVE DESIGN
////////////////////////////////////////////////////////////////////////////////
// These are the styles for phones and tablets
// There are also a couple styles disperesed
@media (max-width: $tablet-width) {
.toc-wrapper {
left: -$nav-width;
&.open {
left: 0;
}
}
.page-wrapper {
margin-left: 0;
}
#nav-button {
display: block;
}
.toc-link {
padding-top: 0.3em;
padding-bottom: 0.3em;
}
}
@media (max-width: $phone-width) {
.dark-box {
display: none;
}
%left-col {
margin-right: 0;
}
.toc-wrapper .lang-selector {
display: block;
}
.page-wrapper .lang-selector {
display: none;
}
%right-col {
width: auto;
float: none;
}
%right-col + %left-col {
margin-top: $main-padding;
}
}
.highlight .c, .highlight .cm, .highlight .c1, .highlight .cs {
color: #909090;
}
.highlight, .highlight .w {
background-color: $code-bg;
}
.copy-clipboard {
float: right;
fill: #9DAAB6;
cursor: pointer;
opacity: 0.4;
height: 18px;
width: 18px;
}
.copy-clipboard:hover {
opacity: 0.8;
}

View File

@ -0,0 +1,9 @@
class Gitea::User::ChangeEmailForm
include ActiveModel::Model
attr_accessor :user
attr_accessor :email, :login
validates :email, presence: true, format: { with: CustomRegexp::EMAIL }
validates :login, presence: true
end

View File

@ -0,0 +1,22 @@
class Gitea::User::ChangePasswordForm
include ActiveModel::Model
attr_accessor :user
attr_accessor :email, :login, :password
validates :email, presence: true, format: { with: CustomRegexp::EMAIL }
validates :login, presence: true
validates :password, presence: true
# validate :check_old_password
def check_old_password
return if user.check_password?(old_password.to_s)
errors.add(:old_password, :password_error)
end
# def check_user!
# user === current_user
# end
end

View File

@ -0,0 +1,18 @@
class Users::SyncGiteaForm
include ActiveModel::Model
attr_accessor :login, :password, :email, :user
validates :login, :password, :email, presence: true
validate :check_user, :check_password
def check_user
user = User.find_by(login: login)
raise '用户不存在.' unless user.present?
end
def check_password
raise '用户密码错误.' unless user.check_password?(password)
end
end

View File

@ -23,13 +23,17 @@ module Gitea
def run
Gitea::UserForm.new(params).validate!
response = Gitea::User::RegisterService.call(params.merge(token: token))
render_result(response)
if response[:status] == :success
@result = response
else
fail!(response[:message])
end
rescue Exception => exception
Rails.logger.info "Exception ===========> #{exception.message}"
fail!(exception.message)
end
private
attr_reader :params
@ -38,10 +42,6 @@ module Gitea
@error = error
end
def render_result(response)
@result = response
end
def token
{
username: Gitea.gitea_config[:access_key_id],

View File

@ -68,11 +68,15 @@ class Ci::Repo < Ci::RemoteBase
repo_timeout: 60,
repo_config: create_params[:repo_config],
repo_created: Time.now.to_i,
repo_updated: Time.now.to_i
repo_updated: Time.now.to_i,
repo_signer: Ci::Repo.generate_code!,
repo_secret: Ci::Repo.generate_code!
)
Rails.logger.info "==========> create_params: #{create_params}"
Rails.logger.info "==========> repo: #{repo.to_json}"
repo.repo_signer = repo.generate_code
repo.repo_secret = repo.generate_code
# repo.repo_signer = repo.generate_code
# repo.repo_secret = repo.generate_code
if repo.save!
Ci::Perm.auto_create!(create_params[:repo_user_id], repo.id)
repo.update_column(:repo_uid, repo.id)
@ -104,4 +108,10 @@ class Ci::Repo < Ci::RemoteBase
def destroy!
self.destroy
end
private
def self.generate_code!
[*'a'..'z',*'0'..'9',*'A'..'Z'].sample(32).join
end
end

View File

@ -39,6 +39,6 @@ class Gitea::Pull < Gitea::Base
serialize :conflicted_files, Array
belongs_to :pull_request, class_name: '::PullRequest', foreign_key: :id, primary_key: :gitea_number, optional: true
belongs_to :pull_request, class_name: '::PullRequest', foreign_key: :id, primary_key: :gitea_id, optional: true
end

View File

@ -1,4 +1,3 @@
# == Schema Information
#
# Table name: projects
@ -38,8 +37,6 @@
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# license_id :integer
# ignore_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
@ -53,9 +50,10 @@
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# license_id :integer
# ignore_id :integer
# default_branch :string(255) default("master")
# website :string(255)
# order_index :integer default("0")
# lesson_url :string(255)
#
# Indexes
@ -79,6 +77,7 @@
class Project < ApplicationRecord
include Matchable
include Publicable
@ -110,6 +109,7 @@ class Project < ApplicationRecord
has_one :project_educoder, dependent: :destroy
has_one :project_score, dependent: :destroy
has_many :project_infos, :dependent => :destroy
has_one :repository, dependent: :destroy
has_many :pull_requests, dependent: :destroy
has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy
@ -125,6 +125,7 @@ class Project < ApplicationRecord
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user
has_many :sonarqubes, dependent: :destroy
after_save :check_project_members, :reset_cache_data
before_save :set_invite_code

View File

@ -0,0 +1,19 @@
# == Schema Information
#
# Table name: project_infos
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class ProjectInfo < ActiveRecord::Base
belongs_to :project
belongs_to :user
validates_presence_of :project_id, :user_id
validates_uniqueness_of :project_id, :scope => :user_id
end

View File

@ -16,7 +16,7 @@
class ProjectUnit < ApplicationRecord
belongs_to :project
enum unit_type: {code: 1, issues: 2, pulls: 3, devops: 4, versions: 5, resources: 6}
enum unit_type: {code: 1, issues: 2, pulls: 3, devops: 4, versions: 5, resources: 6, gantt: 7, sonar: 8}
validates :unit_type, uniqueness: { scope: :project_id}

View File

@ -3,8 +3,8 @@
# Table name: pull_requests
#
# id :integer not null, primary key
# pull_request_id :integer
# gpid :integer
# gitea_id :integer
# gitea_number :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
@ -37,7 +37,7 @@ class PullRequest < ApplicationRecord
has_many :pull_request_tags, foreign_key: :pull_request_id
has_many :project_trends, as: :trend, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
has_one :gitea_pull, foreign_key: :id, primary_key: :gitea_number, class_name: 'Gitea::Pull'
has_one :gitea_pull, foreign_key: :id, primary_key: :gitea_id, class_name: 'Gitea::Pull'
scope :merged_and_closed, ->{where.not(status: 0)}
scope :opening, -> {where(status: 0)}
@ -54,8 +54,10 @@ class PullRequest < ApplicationRecord
Project.find_by(id: self.fork_project_id)
end
def bind_gitea_pull_request!(gitea_pull_number)
update_column(:gitea_number, gitea_pull_number)
def bind_gitea_pull_request!(gitea_pull_number, gitea_pull_id)
update_columns(
gitea_number: gitea_pull_number,
gitea_id: gitea_pull_id)
end
def merge!

18
app/models/sonarqube.rb Normal file
View File

@ -0,0 +1,18 @@
# == Schema Information
#
# Table name: sonarqubes
#
# id :integer not null, primary key
# loophole :integer default("0")
# repetition_rate :integer default("0")
# bug_num :integer default("0")
# file_num :integer default("0")
# project_id :integer
# branch_name :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Sonarqube < ApplicationRecord
belongs_to :project, optional: true
end

View File

@ -157,6 +157,9 @@ class User < Owner
has_many :team_users, dependent: :destroy
has_many :teams, through: :team_users
has_many :projects, foreign_key: :user_id, dependent: :destroy
has_many :repositories, foreign_key: :user_id, dependent: :destroy
# 教学案例
# has_many :libraries, dependent: :destroy
has_many :project_trends, dependent: :destroy
@ -756,6 +759,10 @@ class User < Owner
laboratory_id.present? && laboratory_id != 1
end
def has_gitea_user?
self.gitea_token.present? && self.gitea_uid.present?
end
protected
def validate_password_length
# 管理员的初始密码是5位

View File

@ -24,7 +24,7 @@
#
class Version < ApplicationRecord
belongs_to :project, counter_cache: true, touch: true
belongs_to :project, counter_cache: true, touch: true, optional: true
has_many :issues, class_name: "Issue", foreign_key: "fixed_version_id"
belongs_to :user, optional: true

View File

@ -25,8 +25,7 @@ class Gitea::Repository::CreateService < Gitea::ClientService
private
def request_params
create_params = params.merge(readme: "readme")
Hash.new.merge(token: token, data: create_params)
Hash.new.merge(token: token, data: params)
end
def url

View File

@ -0,0 +1,33 @@
class Gitea::User::GetService < Gitea::ClientService
attr_reader :username
# params:
# username* string # required
def initialize(username)
@username = username
end
def call
response = get(url, params)
status, message, body = render_response(response)
json_format(status, message, body)
end
private
def url
"/users/#{username}"
end
def params
{username: username}
end
def json_format(status, message, body)
case status
when 200 then success(body)
else
error(message, status)
end
end
end

View File

@ -10,8 +10,7 @@ class Gitea::User::RegisterService < Gitea::ClientService
params = Hash.new.merge(data: user_params, token: @token)
response = post(API_REST, params)
status, message, body = render_response(response)
json_format(status, message, body)
response_payload(response)
end
private
@ -27,11 +26,28 @@ class Gitea::User::RegisterService < Gitea::ClientService
}
end
def json_format(status, message, body)
def response_payload(response)
status = response.status
body = response&.body
log_error(status, body)
status_payload(status, body)
end
def status_payload(status, body)
case status
when 201 then success(body)
else
error(message, status)
when 201 then success(json_parse!(body))
when 403 then error("你没有权限操作!")
when 400 then error("服务器开小差了")
when 422
body = json_parse!(body)
message = body['message']
if message.include?('email')
error("邮箱#{email}已被注册")
elsif message.include?('name')
error("用户名#{username}已被注册")
end
else error("系统错误!")
end
end

View File

@ -9,10 +9,11 @@
<% end %>
</td>
<td>
<% if school && school.identifier.present? %>
<% else %>
<%# if school && school.identifier.present? %>
<%#= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
<%# else %>
--
<% end %>
<%# end %>
</td>
<td class="member-container">
<div class="laboratory-user">
@ -29,21 +30,6 @@
</div>
</td>
<td><%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<% if school.present? && laboratory.id != 1 %>
<%= check_box_tag :sync_course,!laboratory.sync_course,laboratory.sync_course,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
<% end %>
</td>
<td>
<% if school.present? && laboratory.id != 1 %>
<%= check_box_tag :sync_subject,!laboratory.sync_subject,laboratory.sync_subject,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
<% end %>
</td>
<td>
<% if school.present? && laboratory.id != 1 %>
<%= check_box_tag :sync_shixun,!laboratory.sync_shixun,laboratory.sync_shixun,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
<% end %>
</td>
<td class="action-container">
<%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory), class: 'action' %>
@ -51,18 +37,4 @@
<%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
<%= link_to '同步用户', synchronize_user_admins_laboratory_path(laboratory), remote: true, data: { confirm: '确认同步该单位下的所有用户到云上实验室吗?' }, class: 'action' %>
<% end %>
<div class="d-inline">
<%= javascript_void_link('更多', class: 'action dropdown-toggle', 'data-toggle': 'dropdown', 'aria-haspopup': true, 'aria-expanded': false) %>
<div class="dropdown-menu more-action-dropdown">
<%= link_to '轮播图', admins_laboratory_carousels_path(laboratory), class: 'dropdown-item' %>
<%= link_to '查看实训项目', admins_laboratory_laboratory_shixuns_path(laboratory), class: 'dropdown-item' %>
<%= link_to '查看实践课程', admins_laboratory_laboratory_subjects_path(laboratory), class: 'dropdown-item' %>
<% if school.present? && laboratory.id != 1 %>
<%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'dropdown-item delete-laboratory-action' %>
<% end %>
</div>
</div>
</td>

View File

@ -7,9 +7,6 @@
<th width="6%">统计链接</th>
<th width="22%">管理员</th>
<th width="10%"><%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %></th>
<th width="4%" title="同步显示主站下该单位的课堂">同步课堂</th>
<th width="4%" title="同步显示主站下该单位用户创建的实践课程">同步实践课程</th>
<th width="4%" title="同步显示主站下该单位用户创建的实训">同步实训</th>
<th width="16%">操作</th>
</tr>
</thead>

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