Compare commits

...

432 Commits

Author SHA1 Message Date
xxq250 991516ed70 Merge pull request '20221101版本' (#312) from Trustie/forgeplus:trustie_server into master 2022-11-03 11:11:34 +08:00
“xxq250” bb0098bba2 Merge branch 'pre_trustie_server' into trustie_server 2022-11-01 18:15:07 +08:00
“xxq250” dfc16bf1f8 Merge branch 'standalone_develop' into pre_trustie_server 2022-11-01 18:14:27 +08:00
“xxq250” 79d46231ba fixed 项目搜索仅公开项目,platform=forge 2022-11-01 18:14:08 +08:00
“xxq250” e27657bcec Merge branch 'pre_trustie_server' into trustie_server 2022-11-01 17:54:03 +08:00
“xxq250” ccaae183b1 Merge branch 'standalone_develop' into pre_trustie_server 2022-11-01 17:53:40 +08:00
“xxq250” 2618dc5798 fixed 项目搜索仅公开项目 2022-11-01 17:51:46 +08:00
“xxq250” 34c8a39678 Merge branch 'pre_trustie_server' into trustie_server 2022-11-01 17:40:18 +08:00
“xxq250” 7d3a745f25 Merge branch 'standalone_develop' into pre_trustie_server 2022-11-01 17:39:44 +08:00
“xxq250” 2bd723fc64 fixed 项目搜索先es search服务容错 2022-11-01 17:37:25 +08:00
“xxq250” 3b7a674f91 Merge branch 'standalone_develop' into pre_trustie_server 2022-11-01 17:33:31 +08:00
“xxq250” 79dae01a69 fixed 项目搜索先es search 中文 2022-11-01 17:31:33 +08:00
“xxq250” 63ea4808c9 fixed 项目搜索先es search 中文 2022-11-01 17:30:01 +08:00
“xxq250” c7a6d78c15 fixed 项目搜索先es search 2022-11-01 17:27:31 +08:00
“xxq250” 3ff3502bbc fixed 项目搜索先es search 2022-11-01 17:25:28 +08:00
viletyy e4f82a072e Merge branch 'pre_trustie_server' into trustie_server 2022-11-01 15:30:15 +08:00
viletyy b73c82f5e5 Merge branch 'standalone_develop' into pre_trustie_server 2022-11-01 15:30:05 +08:00
viletyy 7f91ebb509 Merge branch 'develop' into standalone_develop 2022-11-01 15:29:30 +08:00
yystopf 423d5a9316 修复:同步项目权限到组织时需预先判断是否存在于团队中 2022-11-01 15:29:07 +08:00
viletyy 14831be88b Merge branch 'pre_trustie_server' into trustie_server 2022-10-28 16:08:59 +08:00
viletyy a912106fd6 Merge branch 'standalone_develop' into pre_trustie_server 2022-10-28 16:08:44 +08:00
viletyy 2548b0e9c0 Merge branch 'develop' into standalone_develop 2022-10-27 17:58:27 +08:00
yystopf 9664ca001e 新增: 邮箱验证码重新发送后之前的验证码需设置为无效 2022-10-27 17:57:55 +08:00
viletyy db02c1ec6b Merge branch 'standalone_develop' into pre_trustie_server 2022-10-27 13:51:07 +08:00
viletyy d665d0edb1 Merge branch 'develop' into standalone_develop 2022-10-27 10:49:05 +08:00
yystopf e15809c186 修复:无法正常转移的问题 2022-10-27 10:48:24 +08:00
viletyy 4235c3ee63 Merge branch 'develop' into standalone_develop 2022-10-27 09:56:14 +08:00
yystopf 7d4c0df691 修复 2022-10-27 09:55:55 +08:00
viletyy 761c311a88 Merge branch 'develop' into standalone_develop 2022-10-27 09:30:09 +08:00
yystopf e72dfa4973 修复:项目迁移设置权限在更改拥有者之后 2022-10-27 09:28:52 +08:00
viletyy b29b297f7e Merge branch 'develop' into standalone_develop 2022-10-26 15:16:27 +08:00
yystopf 84a8339a04 修复 2022-10-26 15:16:06 +08:00
viletyy 893f6c04a5 Merge branch 'develop' into standalone_develop 2022-10-26 14:58:25 +08:00
yystopf 3d5b2e76d7 修复:update_column无法触发count_cache 2022-10-26 14:58:04 +08:00
viletyy 6e9962e0da Merge branch 'develop' into standalone_develop 2022-10-26 14:50:23 +08:00
yystopf 8b14ee3c72 修复:更改组织下项目权限同时更改组织下权限同步至gitea 2022-10-26 14:49:40 +08:00
“xxq250” a4c892ec0a fixed cloudIDE saas定制api 2022-10-25 11:19:59 +08:00
“xxq250” 2945570a51 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-10-21 17:44:06 +08:00
“xxq250” 969cbcfe60 fixed users/me返回email,用于三方登录 2022-10-21 17:44:02 +08:00
viletyy 4d61127e49 Merge branch 'develop' into standalone_develop 2022-10-21 15:29:20 +08:00
yystopf 3f7366fbcd 修复:更改用户通知邮箱只能自己改自己的 2022-10-21 15:28:48 +08:00
viletyy 97a661cd95 Merge branch 'develop' into standalone_develop 2022-10-21 15:07:48 +08:00
yystopf cfdbc5a682 修复 2022-10-21 15:07:31 +08:00
viletyy f557994f8a Merge branch 'develop' into standalone_develop 2022-10-21 15:04:52 +08:00
yystopf 9b880e3209 修复 2022-10-21 15:04:26 +08:00
“xxq250” 47aba3528d Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-10-21 14:48:37 +08:00
“xxq250” b80b89e81d fixed 支持refresh_token 2022-10-21 14:48:33 +08:00
viletyy 04642497df Merge branch 'develop' into standalone_develop 2022-10-21 14:43:08 +08:00
yystopf c2ef23b746 修复 2022-10-21 14:42:51 +08:00
viletyy 292167b47e Merge branch 'develop' into standalone_develop 2022-10-21 14:41:09 +08:00
yystopf 8a33ea6707 修复 2022-10-21 14:40:43 +08:00
viletyy 2da84146eb Merge branch 'develop' into standalone_develop 2022-10-21 14:32:03 +08:00
yystopf 9ee4c85b7c 更改:验证返回status以及万能验证码 2022-10-21 14:31:32 +08:00
“xxq250” 4cc4ed5864 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-10-21 09:42:27 +08:00
“xxq250” 0a5dd2f1ee ssh公钥允许token请求 2022-10-21 09:42:21 +08:00
yystopf e9b23d6577 更改:合并请求代码冲突提示语更改 2022-10-19 16:17:44 +08:00
yystopf 64d9c334ce 修复:项目语言验证信息统一 2022-10-19 15:18:15 +08:00
yystopf cad7e24299 修复:项目列表表头样式 2022-10-18 15:53:37 +08:00
yystopf 3b15dd84fc 新增:标签列表返回总数 2022-10-18 14:52:15 +08:00
yystopf 58a43a8b80 更改:合并请求和issue仅加载开启中的里程碑 2022-10-18 13:43:16 +08:00
yystopf e25231738a 新增:标签名字列表 2022-10-18 13:32:57 +08:00
yystopf 35965f542a 新增:标签名字列表 2022-10-18 13:31:12 +08:00
yystopf fa48e3f5ca 修复:许可证和忽略文件内容为空表单提示 2022-10-18 11:14:35 +08:00
yystopf 13e2738fb5 新增:列表页新增only_watched参数 2022-10-17 17:37:03 +08:00
viletyy a494a905fa Merge branch 'develop' into standalone_develop 2022-10-17 13:16:49 +08:00
yystopf 30437d828c 新增:项目git忽略文件新增验证 2022-10-17 13:16:22 +08:00
yystopf dcc7ab6738 更改:项目分类去掉图片上传 2022-10-17 13:01:08 +08:00
yystopf 57a7a17f73 Merge branch 'develop' of https://gitlink.org.cn/Trustie/forgeplus into develop 2022-10-17 12:57:32 +08:00
yystopf 5196f33f24 新增:项目许可证新增验证 2022-10-17 12:57:24 +08:00
“xxq250” b3cbed2963 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	app/controllers/members_controller.rb
2022-10-17 11:09:48 +08:00
“xxq250” dd1af6712c fixed 项目成员提示修改 2022-10-17 11:08:54 +08:00
yystopf 8807be6e4d 新增:项目语言新增唯一性验证 2022-10-17 10:52:57 +08:00
yystopf 8d41c27044 更改:项目成员添加提示 2022-10-17 10:42:40 +08:00
viletyy 77ec7e5a69 Merge branch 'develop' into standalone_develop 2022-10-17 10:25:47 +08:00
yystopf 53fe554d1e 更改:后台管理表头样式 2022-10-17 10:25:17 +08:00
“xxq250” 4e02dbe06b Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	app/controllers/attachments_controller.rb
2022-10-17 10:14:13 +08:00
“xxq250” 620ddff9bb fixed issue评论附件权限 2022-10-17 10:13:30 +08:00
viletyy 732d1a1adb Merge branch 'develop' into standalone_develop 2022-10-15 11:29:58 +08:00
yystopf 630a7aadeb 新增:组织数据修复任务 2022-10-15 11:29:23 +08:00
viletyy 2cdcd5f117 Merge branch 'develop' into standalone_develop 2022-10-14 15:31:39 +08:00
yystopf 6383384287 更改:后台管理用户真实姓名改为昵称 2022-10-14 15:31:20 +08:00
viletyy 6b6c0791cd Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2022-10-14 15:02:59 +08:00
viletyy 2df259c927 Merge branch 'develop' into standalone_develop 2022-10-14 15:02:53 +08:00
yystopf 83f1263f85 修复:更改查询项目判断条件 2022-10-14 14:52:25 +08:00
“xxq250” cc74d53070 Merge branch 'pre_trustie_server' into trustie_server
# Conflicts:
#	lib/tasks/update_educoder_user.rake
2022-10-14 12:03:32 +08:00
“xxq250” 7304e87a11 Merge branch 'standalone_develop' into pre_trustie_server 2022-10-14 12:01:46 +08:00
“xxq250” 4b0609931a 更换发送邮箱 2022-10-14 11:59:48 +08:00
“xxq250” c2742f0ae7 cloudIDE saas定制api 2022-10-12 16:46:15 +08:00
“xxq250” 483c8d5410 cloudIDE saas定制api 2022-10-12 16:42:45 +08:00
“xxq250” c2673bd48d cloudIDE saas定制api 2022-10-12 16:37:54 +08:00
“xxq250” f031cd4720 cloudIDE saas定制api 2022-10-12 16:34:44 +08:00
“xxq250” bf1e722084 cloudIDE saas定制api 2022-10-12 16:04:27 +08:00
“xxq250” 8b1f06f010 cloudIDE saas定制api 2022-10-12 15:58:46 +08:00
“xxq250” 6d2a6e0331 cloudIDE saas定制api 2022-10-12 15:44:46 +08:00
“xxq250” c668fd3530 cloudIDE saas定制api 2022-10-12 15:38:40 +08:00
“xxq250” e7bbe252de cloudIDE saas定制api 2022-10-12 15:37:22 +08:00
yystopf 9c11cd79e8 修复:消息模版无法查询子类的问题 2022-10-10 19:06:47 +08:00
viletyy a516363763 Merge branch 'develop' into standalone_develop 2022-10-08 18:44:28 +08:00
yystopf a7384bc1c3 修复:代码详情页报错 2022-10-08 18:44:04 +08:00
viletyy 3d475fdedd Merge branch 'develop' into standalone_develop 2022-09-29 13:59:04 +08:00
yystopf 55170c7693 新增:更改邮箱相关接口文档 2022-09-29 13:58:14 +08:00
yystopf 2cde13eabb 新增:接口测试以及功能测试 2022-09-29 12:05:57 +08:00
yystopf d68e7f38a3 新增:更改邮箱service逻辑 2022-09-29 09:30:33 +08:00
yystopf 649d9c4ea6 新增:更改邮箱控制器方法 2022-09-28 16:09:10 +08:00
“xxq250” e33448db4d fixed nps统计 2022-09-26 16:53:55 +08:00
“xxq250” bf95b6bca1 fixed 用户活跃数 2022-09-26 16:13:33 +08:00
“xxq250” 3ba7114d08 fixed 用户活跃数 2022-09-26 16:12:52 +08:00
“xxq250” 628d815a5c Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-09-26 16:12:25 +08:00
“xxq250” c08868b26a fixed 用户活跃数 2022-09-26 16:12:12 +08:00
“xxq250” fce8166ba7 fixed 用户活跃数 2022-09-26 16:11:34 +08:00
“xxq250” ae0efc1115 Merge remote-tracking branch 'origin/develop' into develop 2022-09-26 10:38:32 +08:00
“xxq250” dfc2edd62d fixed注册gitea错误信息 2022-09-26 10:38:28 +08:00
yystopf a030a01050 Merge branch 'develop' into standalone_develop 2022-09-23 17:56:58 +08:00
yystopf d7f3a21add 修复:周统计数据需加入年份 2022-09-23 17:56:39 +08:00
“xxq250” aa8a08e272 rake更新用户邮箱和手机号 2022-09-23 10:33:00 +08:00
“xxq250” 72b753d11d rake更新用户邮箱和手机号 2022-09-23 10:32:37 +08:00
“xxq250” 665df16c25 rake更新用户邮箱和手机号 2022-09-23 10:29:00 +08:00
“xxq250” e03071a23d 头歌接口更换密钥 2022-09-23 10:28:59 +08:00
yystopf d9eb335b2a Merge branch 'develop' into standalone_develop 2022-09-23 10:28:19 +08:00
yystopf ef9592ef30 更改:活跃度算法更新 2022-09-23 10:28:12 +08:00
“xxq250” 95ee28b710 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-23 10:26:28 +08:00
yystopf af7a779af6 merge from develop 2022-09-23 10:09:51 +08:00
yystopf cc42683316 新增:活跃度计算访问量限制 2022-09-23 10:07:52 +08:00
xxq250 cf1f78b493 Update README.md 2022-09-22 16:51:56 +08:00
xxq250 88ff9abe49 Update README.md 2022-09-22 16:47:28 +08:00
“xxq250” 28656ad6cd rake更新用户邮箱和手机号 2022-09-21 17:50:05 +08:00
“xxq250” b832a8ecc6 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-09-21 16:00:19 +08:00
“xxq250” 5c123fba17 头歌接口更换密钥 2022-09-21 16:00:13 +08:00
yystopf 7ad9b921d5 Merge branch 'pre_trustie_server' into trustie_server 2022-09-21 10:09:20 +08:00
yystopf b953227e54 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-21 10:09:10 +08:00
yystopf b7193aa768 Merge branch 'develop' into standalone_develop 2022-09-21 10:06:39 +08:00
yystopf 70f1e6827f 更改:项目已设置语言,则不重新设置语言 2022-09-21 10:06:29 +08:00
yystopf e480112c4f Merge branch 'pre_trustie_server' into trustie_server 2022-09-21 09:56:54 +08:00
yystopf fb5938f55d Merge branch 'standalone_develop' into pre_trustie_server 2022-09-21 09:56:40 +08:00
yystopf b4e0cc6dc1 Merge branch 'develop' into standalone_develop 2022-09-21 09:55:05 +08:00
yystopf b5188e3272 Merge branch 'develop' of https://gitlink.org.cn/Trustie/forgeplus into develop 2022-09-21 09:54:53 +08:00
yystopf ff881732d2 更改:项目已设置语言,则不重新设置语言 2022-09-21 09:54:49 +08:00
“xxq250” 2adf87a289 fixed nps数据统计 2022-09-20 09:52:40 +08:00
“xxq250” b3afa15d2e fixed nps数据统计 2022-09-20 09:52:14 +08:00
“xxq250” a8b265c6ce fixed nps数据统计 2022-09-20 09:51:36 +08:00
“xxq250” 3725f88306 fixed nps数据统计 2022-09-20 09:50:21 +08:00
“xxq250” 5a62416228 Merge branch 'pre_trustie_server' into trustie_server 2022-09-20 09:48:57 +08:00
“xxq250” b3506a9bc6 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-20 09:48:28 +08:00
“xxq250” b40f48f87e Merge branch 'develop' into standalone_develop 2022-09-20 09:48:10 +08:00
“xxq250” 6954a860af fixed nps数据统计 2022-09-20 09:47:29 +08:00
“xxq250” 708a0649b3 Merge branch 'pre_trustie_server' into trustie_server 2022-09-20 09:44:29 +08:00
“xxq250” 6ba2d5d212 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-20 09:44:02 +08:00
“xxq250” b07e5d6993 Merge remote-tracking branch 'origin/develop' into standalone_develop 2022-09-20 09:43:20 +08:00
“xxq250” bb8e6aeaf8 nps数据统计 2022-09-20 09:41:27 +08:00
“xxq250” ac8959f36f fixed 修改密码接口 2022-09-19 15:51:59 +08:00
“xxq250” 6d7b7dd058 fixed 修改密码接口 2022-09-19 10:06:49 +08:00
yystopf dfc2c68225 新增:管理后台新增类型筛选 2022-09-15 10:21:39 +08:00
yystopf 6a2cfc5fde Merge branch 'develop' into standalone_develop 2022-09-15 10:00:09 +08:00
yystopf 007b90c284 修复:删除旧的逻辑 2022-09-15 10:00:00 +08:00
yystopf 4994179a33 Merge branch 'develop' into standalone_develop 2022-09-15 09:57:34 +08:00
yystopf 481d1b7db0 修复:commit log统计触发规则 2022-09-15 09:57:28 +08:00
yystopf a1b2bdd048 Merge branch 'develop' into standalone_develop 2022-09-15 09:49:51 +08:00
yystopf 733f64eaec 新增:项目排行榜新增提交数算法 2022-09-15 09:44:12 +08:00
yystopf 383660c778 更改:组织缓存信息方式改在控制器里面 2022-09-15 09:38:28 +08:00
“xxq250” 063861fd38 Merge branch 'pre_trustie_server' into trustie_server 2022-09-09 18:18:43 +08:00
“xxq250” b91aee2710 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-09 18:18:19 +08:00
“xxq250” 2c2c45476d oauth2认证登录logo 2022-09-09 17:55:44 +08:00
“xxq250” de9b3bc511 Merge branch 'pre_trustie_server' into trustie_server 2022-09-08 17:36:16 +08:00
“xxq250” d862a255ff Merge branch 'standalone_develop' into pre_trustie_server 2022-09-08 17:35:39 +08:00
yystopf ecd43dbd12 Merge branch 'develop' into standalone_develop 2022-09-08 14:19:16 +08:00
yystopf 4bd77a870b 更改:webhook创建数量限制为50个 2022-09-08 14:19:08 +08:00
yystopf 9022fdcb80 Merge branch 'develop' into standalone_develop 2022-09-08 10:41:02 +08:00
yystopf b1f460d760 更改:webhook创建数量限制为50个 2022-09-08 10:40:52 +08:00
“xxq250” 353c309243 Merge branch 'pre_trustie_server' into trustie_server 2022-09-07 11:28:14 +08:00
“xxq250” f59d4df7e0 Merge branch 'standalone_develop' into pre_trustie_server 2022-09-07 11:27:48 +08:00
“xxq250” dd35a053cf logo调整 2022-09-07 11:27:13 +08:00
“xxq250” 9a19046e96 Merge branch 'pre_trustie_server' into trustie_server 2022-09-07 11:15:52 +08:00
“xxq250” 1ade73f21a Merge branch 'standalone_develop' into pre_trustie_server 2022-09-07 11:15:18 +08:00
“xxq250” 9c5b899205 Merge branch 'develop' into pre_trustie_server 2022-09-07 11:14:34 +08:00
“xxq250” 00e5735f0a Merge branch 'develop' into pre_trustie_server 2022-09-07 11:13:53 +08:00
“xxq250” 4a08c9be88 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-09-07 11:13:00 +08:00
“xxq250” 200d5e0d69 logo透明图 2022-09-07 11:12:56 +08:00
yystopf a0413be4b9 Merge branch 'develop' into standalone_develop 2022-09-06 14:58:02 +08:00
yystopf a438ab6f3e 新增:提示语locale配置 2022-09-06 14:57:54 +08:00
“xxq250” 3a34d8682c Merge remote-tracking branch 'origin/develop' into develop 2022-09-06 10:30:15 +08:00
“xxq250” 60894f00b1 nps已打分区分 2022-09-06 10:29:51 +08:00
“xxq250” 925b05e004 nps已打分区分 2022-09-06 10:29:49 +08:00
yystopf ce432d9ad3 Merge branch 'develop' into standalone_develop 2022-09-06 09:04:54 +08:00
yystopf 4878d66620 修复 2022-09-06 09:04:46 +08:00
yystopf d240d4f4fa Merge branch 'develop' into standalone_develop 2022-09-06 09:02:37 +08:00
yystopf 632ade9d25 修复 2022-09-06 09:02:30 +08:00
yystopf 36443a54d8 Merge branch 'develop' into standalone_develop 2022-09-06 09:00:24 +08:00
yystopf 5acd45bf85 修复 2022-09-06 09:00:05 +08:00
yystopf d03b45eecf 新增:创建文件对分支长度进行验证 2022-09-06 08:59:24 +08:00
“xxq250” f2b55977c9 nps已打分区分 2022-09-05 13:44:26 +08:00
“xxq250” fd66a3149d nps已打分区分 2022-09-05 13:44:23 +08:00
“xxq250” 84a06fcdc4 nps已打分区分 2022-09-05 13:43:33 +08:00
“xxq250” f412172438 nps已打分区分 2022-09-05 13:43:33 +08:00
“xxq250” e71a3f6864 nps已打分区分 2022-09-05 11:42:57 +08:00
“xxq250” 115277bd64 nps已打分区分 2022-09-05 11:38:23 +08:00
“xxq250” 7c7369730b Merge branch 'pre_trustie_server' into trustie_server 2022-09-02 16:10:30 +08:00
“xxq250” ba3c7169e8 后端js压缩 2022-09-02 16:09:41 +08:00
“xxq250” 9e4100bfab Merge branch 'standalone_develop' into pre_trustie_server 2022-09-02 16:02:23 +08:00
“xxq250” 4a6e94a98f 调整nps用户调研接口,30天 2022-09-02 15:50:15 +08:00
“xxq250” 6857f73384 优化部署流程 2022-09-02 11:26:24 +08:00
“xxq250” 2ac6cdf69d 优化部署流程 2022-09-02 11:15:35 +08:00
“xxq250” c60e80da98 调整nps用户调研接口,测试30分钟 2022-09-01 15:13:49 +08:00
“xxq250” eb1a3f8e23 调整nps用户调研接口,需要登录 2022-08-31 10:40:25 +08:00
“xxq250” a7c24dea64 调整nps用户调研接口 2022-08-31 10:40:24 +08:00
“xxq250” 32a9d762d8 调整nps用户调研接口,需要登录 2022-08-31 10:39:52 +08:00
“xxq250” 0a79950046 调整nps用户调研接口 2022-08-31 10:38:21 +08:00
“xxq250” d51bb7030f Merge remote-tracking branch 'origin/develop' into standalone_develop
# Conflicts:
#	app/views/users/get_user_info.json.jbuilder
2022-08-30 18:14:17 +08:00
“xxq250” 4b7c58b13f Merge remote-tracking branch 'origin/develop' into develop 2022-08-30 18:10:33 +08:00
“xxq250” 7148957368 nps用户调研 2022-08-30 18:10:27 +08:00
yystopf 1613cf7376 Merge branch 'develop' into standalone_develop 2022-08-30 10:40:37 +08:00
yystopf aa50c50254 修复 2022-08-30 10:40:27 +08:00
yystopf 43a41399ac 新增:添加全部或者移除全部团队项目 2022-08-30 10:35:43 +08:00
yystopf 60b3c7925d 新增:添加全部或者移除全部团队项目[文档] 2022-08-30 10:34:37 +08:00
yystopf 0f41cabe71 新增:添加全部或者移除全部团队项目 2022-08-30 10:23:35 +08:00
“xxq250” 8d6106623b Merge branch 'pre_trustie_server' into trustie_server 2022-08-29 10:11:02 +08:00
“xxq250” f9abaaafef Merge branch 'standalone_develop' into pre_trustie_server 2022-08-29 10:10:29 +08:00
“xxq250” 8f4cb2667f fixed 组织成员列表查询优化 2022-08-29 10:08:25 +08:00
“xxq250” d04ed7e008 fixed 组织成员列表查询优化 2022-08-29 10:05:56 +08:00
yystopf 20c154f07c Merge branch 'trustie_server' of https://gitlink.org.cn/Trustie/forgeplus into trustie_server 2022-08-24 17:56:55 +08:00
yystopf 3445838e80 Merge branch 'pre_trustie_server' into trustie_server 2022-08-24 17:56:48 +08:00
yystopf 0a170b6560 Merge branch 'standalone_develop' into pre_trustie_server 2022-08-24 17:56:17 +08:00
yystopf 5c5b4b5d92 新增:分页数量限制修改 2022-08-24 10:30:23 +08:00
yystopf 43e07ade99 Merge branch 'develop' into standalone_develop 2022-08-22 10:48:13 +08:00
yystopf 6168256897 修复: diff filepath为空报错 2022-08-22 10:48:05 +08:00
yystopf 7a7a1efc46 Merge branch 'develop' into standalone_develop 2022-08-17 18:39:08 +08:00
yystopf 4b167dacbb 修复: trace user 手机号为空 2022-08-17 18:39:01 +08:00
yystopf 98446220a1 Merge branch 'develop' into standalone_develop 2022-08-17 18:05:22 +08:00
yystopf 911c47df8d 新增:文件blame 上个版本的行号 2022-08-17 18:05:15 +08:00
“xxq250” 0dbc0ed098 Merge branch 'pre_trustie_server' into trustie_server 2022-08-17 16:12:35 +08:00
“xxq250” 3ba482647e Merge branch 'standalone_develop' into pre_trustie_server 2022-08-17 16:12:10 +08:00
yystopf 1e4aed71fe Merge branch 'develop' into standalone_develop 2022-08-17 15:29:16 +08:00
yystopf a37d7b9840 修复: 手机号默认不传 2022-08-17 15:28:58 +08:00
“xxq250” dcfb39a6b1 Merge branch 'pre_trustie_server' into trustie_server 2022-08-16 15:39:53 +08:00
“xxq250” d42acc9aa9 Merge branch 'standalone_develop' into pre_trustie_server 2022-08-16 15:38:56 +08:00
“xxq250” c7e098d507 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-08-16 15:33:59 +08:00
“xxq250” dbdb860bb7 fixed commit日志同时更新项目时间 2022-08-16 15:33:46 +08:00
“xxq250” 217ab920a9 fixed commit日志同时更新项目时间 2022-08-16 15:33:15 +08:00
“xxq250” 98ae62d181 Merge branch 'develop' of https://gitlink.org.cn/Trustie/forgeplus into develop 2022-08-16 14:28:52 +08:00
“xxq250” 98f1723411 fixed 发行版git删除时,不同步处理 2022-08-16 14:28:40 +08:00
yystopf 144fbac843 Merge branch 'develop' into standalone_develop 2022-08-16 14:07:49 +08:00
yystopf 68a38e75bc 修复: 消息返回接口时间 2022-08-16 14:07:37 +08:00
“xxq250” 81b64c18b6 fixed 发行版git删除时,不同步处理log 2022-08-16 14:03:05 +08:00
“xxq250” 0c10f3ff10 fixed 发行版git删除时,不同步处理log 2022-08-16 11:50:09 +08:00
“xxq250” c6034ed33f fixed 发行版git删除时,不同步处理test 2022-08-16 11:48:39 +08:00
“xxq250” 4618f4effa fixed 发行版git删除时,不同步处理 2022-08-16 11:46:58 +08:00
“xxq250” b2af43ee2a 增加建木devops地址配置 2022-08-16 11:34:20 +08:00
“xxq250” f211fbaec3 增加建木devops地址配置URI.encode 2022-08-16 11:31:43 +08:00
“xxq250” 14c1be1bea 增加建木devops地址配置 2022-08-16 11:29:33 +08:00
yystopf 63cdaed3ac 更改:代码溯源status改为code 2022-08-15 15:56:07 +08:00
yystopf ee29c6be5c Merge branch 'develop' of https://gitlink.org.cn/Trustie/forgeplus into develop 2022-08-15 15:55:37 +08:00
yystopf c0ee0a694c 更改:代码溯源status改为code 2022-08-15 15:55:33 +08:00
“xxq250” 12cc870dc7 fixed time ago优化 2022-08-15 15:47:30 +08:00
“xxq250” e5989b219f fixed reposyncer job任务增加分页 2022-08-15 14:04:06 +08:00
“xxq250” 25fa6f427b Merge remote-tracking branch 'origin/develop' into develop 2022-08-15 14:03:32 +08:00
“xxq250” 467d722d25 fixed reposyncer job任务增加分页 2022-08-15 14:03:27 +08:00
yystopf 04cd427c5f 新增:代码溯源需提前获取服务器是否在维护 2022-08-12 18:15:59 +08:00
yystopf 9992a78161 Merge branch 'develop' into standalone_develop 2022-08-12 17:24:15 +08:00
yystopf 3e352d3c7c 新增:获取代码溯源系统维护信息接口 2022-08-12 17:23:19 +08:00
“xxq250” f386274584 Merge branch 'pre_trustie_server' into trustie_server 2022-08-12 15:45:48 +08:00
“xxq250” 092e3e2546 Merge branch 'standalone_develop' into pre_trustie_server 2022-08-12 15:44:45 +08:00
viletyy 6bb1dcf718 Merge remote-tracking branch 'origin/develop' into standalone_develop 2022-08-11 15:21:37 +08:00
yystopf 13472b2311 新增: 合并请求评论查询is_full 2022-08-11 15:21:11 +08:00
viletyy 71b42e462e merge from develop 2022-08-11 09:52:43 +08:00
“xxq250” bdddd655bf Merge remote-tracking branch 'origin/develop' into develop 2022-08-11 09:31:49 +08:00
“xxq250” 6ec7a13293 fixed reposyncer 分支组合已配置,不能重复 2022-08-11 09:31:40 +08:00
“xxq250” 716b29c25f fixed 消息模板配置type 2022-08-11 09:29:37 +08:00
“xxq250” 5c4930a362 fixed 消息模板配置type 2022-08-11 09:29:36 +08:00
“xxq250” ad8fed9440 fixed 消息模板配置type 2022-08-11 09:29:35 +08:00
“xxq250” 964bef7896 fixed 消息模板配置type 2022-08-11 09:29:34 +08:00
“xxq250” 7e48e5e8b8 fixed 消息模板配置 2022-08-11 09:29:34 +08:00
“xxq250” 48846a7e31 fixed 已读未读标记优化 2022-08-11 09:29:33 +08:00
yystopf 32db15aad5 新增: commit找不到返回404 2022-08-11 09:29:12 +08:00
“xxq250” 8ec3e4bbaa fixed Gitea::PullRequest::FilesService增加参数 2022-08-11 09:28:12 +08:00
“xxq250” c167c39050 fixed 消息模板配置type 2022-08-10 17:43:27 +08:00
“xxq250” 58368277c0 fixed 消息模板配置type 2022-08-10 17:25:55 +08:00
“xxq250” 4918fda2e1 fixed 消息模板配置type 2022-08-10 17:23:27 +08:00
“xxq250” 9ce7cdf0c3 fixed 消息模板配置type 2022-08-10 17:20:29 +08:00
“xxq250” e46c934b2c fixed 消息模板配置 2022-08-10 16:54:00 +08:00
“xxq250” b949ee260d fixed 已读未读标记优化 2022-08-10 16:07:20 +08:00
“xxq250” c60572e31f fixed Gitea::PullRequest::FilesService增加参数 2022-08-10 16:06:11 +08:00
“xxq250” 1efff8a97a Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-08-09 10:23:01 +08:00
“xxq250” 4199f4097c fixed reposyncer gitlink地址配置文件取 2022-08-09 10:22:55 +08:00
yystopf a0dcc289c8 Merge branch 'develop' into standalone_develop 2022-08-09 09:48:14 +08:00
yystopf 4b05838498 新增:合并请求评论新增path参数筛选以及去掉分页最多返回200数据 2022-08-09 09:48:02 +08:00
yystopf 0699c768a5 新增:fork仓库不支持代码溯源 2022-08-08 14:17:45 +08:00
“xxq250” e7aa05b535 fixed reposyncer 删除job任务 2022-08-05 13:48:40 +08:00
“xxq250” 99ba523084 fixed reposyncer job_logs 2022-08-05 11:45:46 +08:00
“xxq250” 14f4b290cb fixed reposyncer job_logs 2022-08-05 11:42:40 +08:00
“xxq250” 5ffc603f61 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-08-05 11:41:18 +08:00
“xxq250” eac06382fe fixed reposyncer logs routes 2022-08-05 11:41:13 +08:00
yystopf 65e1cd2c01 Merge branch 'develop' into standalone_develop 2022-08-05 11:05:37 +08:00
yystopf b8be0c23cb 修复 2022-08-05 11:05:29 +08:00
yystopf d4405b8827 Merge branch 'develop' into standalone_develop 2022-08-05 10:50:52 +08:00
yystopf d20a7b1318 修复:项目协作者添加组织成员问题 2022-08-05 10:50:41 +08:00
yystopf 0993e5a3ae Merge branch 'develop' into standalone_develop 2022-08-04 17:28:29 +08:00
yystopf f8268accc0 新增: 组织下项目协作者添加当前用户 2022-08-04 17:28:14 +08:00
“xxq250” 690ddcb4a7 fixed reposyncer 分支组合已配置,不能重复 2022-08-04 17:20:24 +08:00
“xxq250” 72a4549fe9 fixed reposyncer jobs 分组 2022-08-04 17:02:25 +08:00
“xxq250” 4fb2003f64 fixed reposyncer 分支组合已配置,不能重复 2022-08-04 16:50:01 +08:00
“xxq250” 5c361207f5 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-08-04 16:24:25 +08:00
“xxq250” 4941e08684 fixed reposyncer job_type 2022-08-04 16:24:19 +08:00
yystopf 7e711cfdab Merge branch 'develop' into standalone_develop 2022-08-04 15:46:32 +08:00
yystopf a9811768d4 修复: 合并请求评论类型允许为空 2022-08-04 15:46:19 +08:00
“xxq250” ebe8df8221 fixed reposyncer delete 2022-08-04 11:24:15 +08:00
“xxq250” 1b3a4bc146 fixed reposyncer gitlink地址 2022-08-04 10:37:28 +08:00
“xxq250” 7332e4a58b Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-08-04 10:03:34 +08:00
“xxq250” 1a2352c535 fixed reposyncer gitlink地址 2022-08-04 10:03:29 +08:00
yystopf 958ee42fa2 Merge branch 'develop' into standalone_develop 2022-08-03 17:13:53 +08:00
yystopf 024bc289de 修改: 合并请求评论必选参数以及审查列表去掉分页 2022-08-03 17:13:45 +08:00
yystopf 90a621ed8e Merge branch 'develop' into standalone_develop 2022-08-03 15:31:14 +08:00
yystopf 96d34d0237 修改:合并请求审查权限 2022-08-03 15:31:04 +08:00
yystopf 64e608ffd7 Merge branch 'develop' into standalone_develop 2022-08-03 14:20:39 +08:00
yystopf fea11bdc2e 新增:webhook type类型 2022-08-03 14:20:25 +08:00
yystopf 0be06c8282 Merge branch 'develop' into standalone_develop 2022-08-02 19:04:04 +08:00
yystopf ab5d273f98 新增:版本diff新增diff信息 2022-08-02 19:03:54 +08:00
“xxq250” 1675c3ad23 fixed Repository.url 2022-08-02 18:12:07 +08:00
“xxq250” 8bf4e9b8d2 fixed Repository.url 2022-08-02 18:10:01 +08:00
“xxq250” bf6a11dadd Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2022-08-02 15:27:31 +08:00
“xxq250” 9d385845a6 fixed 评审标记为已读和未读 2022-08-02 15:27:16 +08:00
yystopf d4d8153003 Merge branch 'develop' into standalone_develop 2022-08-01 17:14:41 +08:00
yystopf 096b0b954d 新增: fork仓库协作者可以提交文件至仓库 2022-08-01 17:14:32 +08:00
yystopf b06e29260a 修复: return error status默认为-1 2022-08-01 14:28:44 +08:00
yystopf 1410ba144c Merge branch 'pre_trustie_server' into trustie_server 2022-07-29 18:53:52 +08:00
yystopf 736fe6d9e3 Merge branch 'trustie_server' of https://gitlink.org.cn/Trustie/forgeplus into trustie_server 2022-07-29 18:53:44 +08:00
yystopf 275456bf5f Merge branch 'standalone_develop' into pre_trustie_server 2022-07-29 18:53:33 +08:00
yystopf 29bfeabb20 merge from develop 2022-07-29 17:29:52 +08:00
yystopf df57312056 修复 2022-07-29 17:29:25 +08:00
“xxq250” 7597f1fd8f fixed 建木认证code,项目成员可见 2022-07-29 16:30:56 +08:00
“xxq250” 8dba81bfca fixed 建木认证code加密生成,key=Client Secret 2022-07-29 15:37:24 +08:00
“xxq250” 531747024d fixed 建木认证code加密生成,key=Client Secret 2022-07-29 15:35:12 +08:00
“xxq250” a560a5034c fixed 建木认证code加密生成 2022-07-29 14:20:56 +08:00
“xxq250” 34504ddc39 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-07-29 14:18:58 +08:00
“xxq250” 29342e11fb 建木认证code加密生成 2022-07-29 14:18:53 +08:00
yystopf adb55c0910 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2022-07-28 18:14:05 +08:00
yystopf 902c66d490 更改: oauth2授权页面增加错误提示 2022-07-28 18:14:00 +08:00
“xxq250” 8a187df5f4 fixed CloudIDEA文件标记已读未读列表 2022-07-28 17:59:20 +08:00
“xxq250” 93fd9333ec fixed CloudIDEA文件标记已读未读列表 2022-07-28 17:56:44 +08:00
“xxq250” 487c6dc21a fixed CloudIDEA文件标记已读未读列表 2022-07-28 17:55:31 +08:00
“xxq250” 0936bbd1c8 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-07-28 17:53:48 +08:00
“xxq250” 2b31b683dd fixed CloudIDEA文件标记已读未读列表 2022-07-28 17:53:43 +08:00
“xxq250” 27fae8d27b fixed 精选项目不包含私有仓库 2022-07-28 17:31:07 +08:00
“xxq250” f12cfabee3 fixed 精选项目不包含私有仓库 2022-07-28 17:30:09 +08:00
“xxq250” b22909a654 fixed 语言接口token参数错误 2022-07-28 17:20:22 +08:00
“xxq250” 579b45dfa4 fixed 精选项目不包含私有仓库 2022-07-28 17:19:39 +08:00
“xxq250” 99850e45ad fixed 语言接口token参数错误 2022-07-28 17:15:34 +08:00
“xxq250” 0908e61f59 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-07-28 16:46:54 +08:00
“xxq250” 321f367e3e Merge remote-tracking branch 'origin/develop' into standalone_develop 2022-07-28 16:46:27 +08:00
“xxq250” fbc1f95cd1 Merge remote-tracking branch 'origin/develop' into develop 2022-07-28 16:42:29 +08:00
“xxq250” 4ccf8ea5ff CloudIDEA文件标记已读未读 2022-07-28 16:42:21 +08:00
yystopf bc580e1bac Merge branch 'develop' into standalone_develop 2022-07-28 15:19:43 +08:00
yystopf d5c141f892 修复:pulls文件夹load 2022-07-28 15:19:31 +08:00
yystopf 2d0f1ab94c Merge branch 'develop' into standalone_develop 2022-07-28 14:25:43 +08:00
yystopf 35c6cb98db 新增:批量提交文件支持参数选填 2022-07-28 14:25:33 +08:00
yystopf c93c4ad657 Merge branch 'develop' into standalone_develop 2022-07-28 14:02:45 +08:00
yystopf f417e42864 修复: 提示修改 2022-07-28 14:02:37 +08:00
yystopf bc0222ac21 Merge branch 'develop' into standalone_develop 2022-07-28 13:56:43 +08:00
yystopf 6e54203ebb 修复:批量提交文件参数缺失以及数据为空处理 2022-07-28 13:56:31 +08:00
“xxq250” 79a6841bbc Merge remote-tracking branch 'origin/develop' into develop 2022-07-28 09:31:19 +08:00
“xxq250” b3d78741a3 fixed reposyncer同步仓库接口增加gitlink 2022-07-28 09:31:14 +08:00
yystopf 445ae6fbc3 merge from develop 2022-07-27 16:12:12 +08:00
yystopf 935f3ddf7f Merge pull request '合并请求评论相关接口及文档' (#20) from yystopf/forgeplus:develop into develop 2022-07-27 16:11:17 +08:00
yystopf a2549f7ff1 新增: 评论及审查相关接口文档 2022-07-27 16:07:15 +08:00
yystopf d6b5dc326a 新增: 评论相关api根据需求重写 2022-07-27 14:52:49 +08:00
“xxq250” d6c6ac9a0a fixed reposyncer同步仓库接口增加gitlink 2022-07-26 18:13:29 +08:00
“xxq250” f3233b145b fixed reposyncer同步仓库接口 2022-07-26 18:11:09 +08:00
“xxq250” d8d841d800 fixed reposyncer同步仓库接口 2022-07-26 18:10:35 +08:00
“xxq250” bcea5bc669 fixed 邮箱验证正则优化 2022-07-26 17:11:46 +08:00
“xxq250” 003bfe7884 Merge remote-tracking branch 'origin/develop' into develop 2022-07-26 17:08:10 +08:00
“xxq250” df5efa0dc6 fixed 邮箱验证正则优化 2022-07-26 17:08:05 +08:00
yystopf a35db9fe80 新增:reviewers修改入口 2022-07-26 16:45:53 +08:00
“xxq250” 5be93d5de8 关闭rack-mini-profiler 2022-07-26 16:32:53 +08:00
“xxq250” 99d9e13edc Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-07-26 15:44:11 +08:00
“xxq250” 4686b5241e fixed 同步仓库增加gitlink-url 2022-07-26 15:44:04 +08:00
yystopf 5025bf6961 Merge branch 'develop' into standalone_develop 2022-07-26 14:38:50 +08:00
yystopf 6183f1f76d 修复: 缺少title和body 2022-07-26 14:38:41 +08:00
yystopf 034170372e Merge branch 'develop' into standalone_develop 2022-07-26 14:29:21 +08:00
yystopf e72273f3b2 修复: 合并请求详情中review需要最新的一个 2022-07-26 14:29:12 +08:00
yystopf 70220638fd merge from develop 2022-07-26 14:22:18 +08:00
yystopf cc09f724cc Merge branch 'develop' of https://gitlink.org.cn/Trustie/forgeplus into develop 2022-07-26 14:14:33 +08:00
yystopf 7a54c5f536 新增: 接口文档更新 2022-07-26 14:14:25 +08:00
“xxq250” b1809909db fixed reposyncer同步仓库接口 2022-07-26 11:41:21 +08:00
“xxq250” bb3f5cec76 fixed reposyncer同步仓库接口 2022-07-26 11:40:49 +08:00
“xxq250” 19ffd9fea1 fixed PullRequest 统一关联关系字段gitea_id 2022-07-26 10:55:42 +08:00
“xxq250” 6f9db6b314 Merge remote-tracking branch 'origin/develop' into standalone_develop
# Conflicts:
#	app/models/pull_request.rb
2022-07-26 10:49:49 +08:00
“xxq250” 5877474f59 Merge remote-tracking branch 'origin/develop' into develop 2022-07-26 10:44:21 +08:00
“xxq250” 1bbde165c2 reposyncer同步仓库接口 2022-07-26 10:44:12 +08:00
yystopf 3f5754a3a5 新增:合并请求详情接口 2022-07-25 18:23:06 +08:00
yystopf 5463c22518 新增: 合并请求创建审查接口 2022-07-25 17:28:38 +08:00
yystopf 5db43bf768 新增:新合并请求列表接口 2022-07-25 16:21:03 +08:00
yystopf 1feb166fd5 修复:合并请求评审模型修改 2022-07-25 16:20:42 +08:00
yystopf db4e98ec37 修复:代码溯源跳转地址修改 2022-07-25 11:00:07 +08:00
xxq250 802568cf89 Merge pull request '20220722版本' (#309) from Trustie/forgeplus:trustie_server into master 2022-07-22 19:57:44 +08:00
yystopf 56b1110e4d Merge branch 'develop' into standalone_develop 2022-07-22 18:10:09 +08:00
yystopf 0df1e5fdd0 新增:合并请求版本列表和diff接口文档 2022-07-22 18:08:16 +08:00
yystopf 8ba70a33be 新增: pr版本列表和diff接口 2022-07-22 17:33:08 +08:00
yystopf 3a12b7b16c 新增:代码溯源展示页面url返回 2022-07-22 13:44:07 +08:00
yystopf 4aebba2f25 修复:代码溯源cookie domain配置 2022-07-22 11:16:04 +08:00
yystopf 95cf55993d Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2022-07-22 10:22:28 +08:00
yystopf 81b684e469 修复:代码溯源cookie domain 2022-07-22 10:22:22 +08:00
“xxq250” e3e4cb6a5b fixed 代码提交日志根据提交commit记录多次 2022-07-22 10:19:20 +08:00
“xxq250” 0b167026f8 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2022-07-22 10:12:19 +08:00
“xxq250” 913057658e fixed 代码提交日志根据提交commit记录多次 2022-07-22 10:12:13 +08:00
“xxq250” b21f44b59e fixed 代码提交日志根据提交commit记录多次 2022-07-22 10:11:21 +08:00
yystopf 8645642612 新增:代码溯源设置cookie用于访问 2022-07-22 09:35:47 +08:00
yystopf dddab437d3 Merge branch 'pre_trustie_server' into trustie_server 2022-07-21 10:53:06 +08:00
xxq250 db8ab8bc22 Merge pull request '*fix Issue的中文翻译更改同步' (#308) from shenmo7192/forgeplus:master into master 2022-06-15 08:34:32 +08:00
shenmo7192 ba84cc7098 Update README.md *fix: Issue的中文翻译更改同步 2022-06-14 23:30:06 +08:00
xxq250 4a886b4bb1 Merge pull request '20220608版本' (#307) from develop into master 2022-06-08 14:44:09 +08:00
xxq250 507d63e98c Merge pull request '20220608版本' (#306) from Trustie/forgeplus:trustie_server into develop 2022-06-08 14:43:22 +08:00
xxq250 31d7f0a13c Merge pull request '20220507版本' (#305) from Trustie/forgeplus:trustie_server into master 2022-05-07 15:11:36 +08:00
yystopf 153009a63e Merge pull request '更新README文件' (#304) from Nigel/forgeplus:pr-4-gitlink into master 2022-05-07 13:47:00 +08:00
Xunhui Zhang f953883b9b 更新了readme中的gitea二进制文件地址 2022-05-07 11:09:09 +08:00
xxq250 9a8a0b4c5d Merge pull request '20220303版本' (#303) from Trustie/forgeplus:trustie_server into master 2022-03-03 15:33:17 +08:00
xxq250 9b44d90c0a Update README.md 2022-02-24 16:52:01 +08:00
xxq250 cc31d8ed2c Merge pull request '更新readme' (#302) from young/forgeplus:master into master 2022-02-24 16:50:34 +08:00
young 778ec17986 Update README.md 2022-02-24 16:21:25 +08:00
young 86f854dc69 Update README.md 2022-02-24 16:20:17 +08:00
young 9835d69da0 add figs
Signed-off-by: young <1593815610@qq.com>
2022-02-24 16:18:44 +08:00
young defaa1e46b Delete 'docs/figs' 2022-02-24 16:14:34 +08:00
young a13820b229 ADD file via upload 2022-02-24 16:14:22 +08:00
young 541b67f3a5 Delete 'docs/figs/issue_view.png' 2022-02-24 16:13:35 +08:00
young 0ba7524408 Delete 'docs/figs/milestone.png' 2022-02-24 16:13:23 +08:00
young bd553ac532 Delete 'docs/figs/issue_assign2.png' 2022-02-24 16:13:08 +08:00
young a73d4a221a Delete 'docs/figs/issue_manage.png' 2022-02-24 16:12:50 +08:00
young 2d232073e6 Delete 'docs/figs/issue_assign.png' 2022-02-24 16:12:36 +08:00
young e7dc4727ae Delete 'docs/figs/code.png' 2022-02-24 16:12:17 +08:00
young 9ec9529b32 Update README.md 2022-02-24 16:10:17 +08:00
xxq250 137fd5187c Merge pull request 'readme更新' (#300) from young/forgeplus:master into master 2022-02-24 11:16:14 +08:00
xiaoxiaoqiong a88b093353 Merge branch 'trustie_server' 2022-02-22 09:42:54 +08:00
young ba00d31fa6 Update README.md 2021-12-06 10:33:22 +08:00
jasder 4fa10a3683 Merge pull request 'fix mege develop' (#248) from develop into master 2021-11-14 20:39:31 +08:00
jasder cb11a5c8e9 发布v3.1.1版本
* FIX 修复pulls、issues 搜索数量统计问题
* FIX 解决在线创建文件错误提示信息不准确的问题
* FIX table name bug
* FIX 解决applied_projects相关表操作的问题
* FIX 解决排序参数的判断预防SQL注入
* FIX 项目导航栏数量改为开启中的数量
* FIX 解决项目邀请码的索引查询问题
* FIX FIX get readme file bug
* FIX sql attack
* FIX projects load by invite code slowly
2021-06-18 17:09:57 +08:00
270 changed files with 49787 additions and 1249174 deletions

1
.gitignore vendored
View File

@ -81,7 +81,6 @@ db/bak/
docker/
educoder.sql
redis_data/
Dockerfile
dump.rdb
.tags*
ceshi_user.xlsx

33
Dockerfile Normal file
View File

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

View File

@ -98,9 +98,9 @@ gem 'font-awesome-sass', '4.7.0'
gem 'rails-i18n', '~> 5.1'
# job
gem 'sidekiq'
gem 'sidekiq',"5.2.8"
gem 'sinatra'
gem "sidekiq-cron", "~> 1.1"
gem "sidekiq-cron", "1.2.0"
# batch insert
gem 'bulk_insert'
@ -135,4 +135,4 @@ gem 'doorkeeper'
gem 'doorkeeper-jwt'
gem 'gitea-client', '~> 0.10.2'
gem 'gitea-client', '~> 0.10.5'

View File

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

208
README.md
View File

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

View File

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

View File

@ -0,0 +1,180 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function ($) {
'use strict';
// TOGGLE PUBLIC CLASS DEFINITION
// ==============================
var Toggle = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, this.defaults(), options)
this.render()
}
Toggle.VERSION = '2.2.0'
Toggle.DEFAULTS = {
on: 'On',
off: 'Off',
onstyle: 'primary',
offstyle: 'default',
size: 'normal',
style: '',
width: null,
height: null
}
Toggle.prototype.defaults = function() {
return {
on: this.$element.attr('data-on') || Toggle.DEFAULTS.on,
off: this.$element.attr('data-off') || Toggle.DEFAULTS.off,
onstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle,
offstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle,
size: this.$element.attr('data-size') || Toggle.DEFAULTS.size,
style: this.$element.attr('data-style') || Toggle.DEFAULTS.style,
width: this.$element.attr('data-width') || Toggle.DEFAULTS.width,
height: this.$element.attr('data-height') || Toggle.DEFAULTS.height
}
}
Toggle.prototype.render = function () {
this._onstyle = 'btn-' + this.options.onstyle
this._offstyle = 'btn-' + this.options.offstyle
var size = this.options.size === 'large' ? 'btn-lg'
: this.options.size === 'small' ? 'btn-sm'
: this.options.size === 'mini' ? 'btn-xs'
: ''
var $toggleOn = $('<label class="btn">').html(this.options.on)
.addClass(this._onstyle + ' ' + size)
var $toggleOff = $('<label class="btn">').html(this.options.off)
.addClass(this._offstyle + ' ' + size + ' active')
var $toggleHandle = $('<span class="toggle-handle btn btn-default">')
.addClass(size)
var $toggleGroup = $('<div class="toggle-group">')
.append($toggleOn, $toggleOff, $toggleHandle)
var $toggle = $('<div class="toggle btn" data-toggle="toggle">')
.addClass( this.$element.prop('checked') ? this._onstyle : this._offstyle+' off' )
.addClass(size).addClass(this.options.style)
this.$element.wrap($toggle)
$.extend(this, {
$toggle: this.$element.parent(),
$toggleOn: $toggleOn,
$toggleOff: $toggleOff,
$toggleGroup: $toggleGroup
})
this.$toggle.append($toggleGroup)
var width = this.options.width || Math.max($toggleOn.outerWidth(), $toggleOff.outerWidth())+($toggleHandle.outerWidth()/2)
var height = this.options.height || Math.max($toggleOn.outerHeight(), $toggleOff.outerHeight())
$toggleOn.addClass('toggle-on')
$toggleOff.addClass('toggle-off')
this.$toggle.css({ width: width, height: height })
if (this.options.height) {
$toggleOn.css('line-height', $toggleOn.height() + 'px')
$toggleOff.css('line-height', $toggleOff.height() + 'px')
}
this.update(true)
this.trigger(true)
}
Toggle.prototype.toggle = function () {
if (this.$element.prop('checked')) this.off()
else this.on()
}
Toggle.prototype.on = function (silent) {
if (this.$element.prop('disabled')) return false
this.$toggle.removeClass(this._offstyle + ' off').addClass(this._onstyle)
this.$element.prop('checked', true)
if (!silent) this.trigger()
}
Toggle.prototype.off = function (silent) {
if (this.$element.prop('disabled')) return false
this.$toggle.removeClass(this._onstyle).addClass(this._offstyle + ' off')
this.$element.prop('checked', false)
if (!silent) this.trigger()
}
Toggle.prototype.enable = function () {
this.$toggle.removeAttr('disabled')
this.$element.prop('disabled', false)
}
Toggle.prototype.disable = function () {
this.$toggle.attr('disabled', 'disabled')
this.$element.prop('disabled', true)
}
Toggle.prototype.update = function (silent) {
if (this.$element.prop('disabled')) this.disable()
else this.enable()
if (this.$element.prop('checked')) this.on(silent)
else this.off(silent)
}
Toggle.prototype.trigger = function (silent) {
this.$element.off('change.bs.toggle')
if (!silent) this.$element.change()
this.$element.on('change.bs.toggle', $.proxy(function() {
this.update()
}, this))
}
Toggle.prototype.destroy = function() {
this.$element.off('change.bs.toggle')
this.$toggleGroup.remove()
this.$element.removeData('bs.toggle')
this.$element.unwrap()
}
// TOGGLE PLUGIN DEFINITION
// ========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.toggle')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.toggle', (data = new Toggle(this, options)))
if (typeof option == 'string' && data[option]) data[option]()
})
}
var old = $.fn.bootstrapToggle
$.fn.bootstrapToggle = Plugin
$.fn.bootstrapToggle.Constructor = Toggle
// TOGGLE NO CONFLICT
// ==================
$.fn.toggle.noConflict = function () {
$.fn.bootstrapToggle = old
return this
}
// TOGGLE DATA-API
// ===============
$(function() {
$('input[type=checkbox][data-toggle^=toggle]').bootstrapToggle()
})
$(document).on('click.bs.toggle', 'div[data-toggle^=toggle]', function(e) {
var $checkbox = $(this).find('input[type=checkbox]')
$checkbox.bootstrapToggle('toggle')
e.preventDefault()
})
}(jQuery);

View File

@ -0,0 +1,9 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
//# sourceMappingURL=bootstrap-toggle.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"bootstrap-toggle.min.js","sources":["bootstrap-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","outerHeight","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":";;;;;;;CASE,SAAUA,GACV,YAoID,SAASC,GAAOC,GACf,MAAOC,MAAKC,KAAK,WAChB,GAAIC,GAAUL,EAAEG,MACZG,EAAUD,EAAMC,KAAK,aACrBC,EAA2B,gBAAVL,IAAsBA,CAEtCI,IAAMD,EAAMC,KAAK,YAAcA,EAAO,GAAIE,GAAOL,KAAMI,IACvC,gBAAVL,IAAsBI,EAAKJ,IAASI,EAAKJ,OAtItD,GAAIM,GAAS,SAAUC,EAASF,GAC/BJ,KAAKO,SAAYV,EAAES,GACnBN,KAAKI,QAAYP,EAAEW,UAAWR,KAAKS,WAAYL,GAC/CJ,KAAKU,SAGNL,GAAOM,QAAW,QAElBN,EAAOO,UACNC,GAAI,KACJC,IAAK,MACLC,QAAS,UACTC,SAAU,UACVC,KAAM,SACNC,MAAO,GACPC,MAAO,KACPC,OAAQ,MAGTf,EAAOgB,UAAUZ,SAAW,WAC3B,OACCI,GAAIb,KAAKO,SAASe,KAAK,YAAcjB,EAAOO,SAASC,GACrDC,IAAKd,KAAKO,SAASe,KAAK,aAAejB,EAAOO,SAASE,IACvDC,QAASf,KAAKO,SAASe,KAAK,iBAAmBjB,EAAOO,SAASG,QAC/DC,SAAUhB,KAAKO,SAASe,KAAK,kBAAoBjB,EAAOO,SAASI,SACjEC,KAAMjB,KAAKO,SAASe,KAAK,cAAgBjB,EAAOO,SAASK,KACzDC,MAAOlB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASM,MAC3DC,MAAOnB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASO,MAC3DC,OAAQpB,KAAKO,SAASe,KAAK,gBAAkBjB,EAAOO,SAASQ,SAI/Df,EAAOgB,UAAUX,OAAS,WACzBV,KAAKuB,SAAW,OAASvB,KAAKI,QAAQW,QACtCf,KAAKwB,UAAY,OAASxB,KAAKI,QAAQY,QACvC,IAAIC,GAA6B,UAAtBjB,KAAKI,QAAQa,KAAmB,SAClB,UAAtBjB,KAAKI,QAAQa,KAAmB,SACV,SAAtBjB,KAAKI,QAAQa,KAAkB,SAC/B,GACCQ,EAAY5B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQS,IACzDc,SAAS3B,KAAKuB,SAAW,IAAMN,GAC7BW,EAAa/B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQU,KAC1Da,SAAS3B,KAAKwB,UAAY,IAAMP,EAAO,WACrCY,EAAgBhC,EAAE,gDACpB8B,SAASV,GACPa,EAAejC,EAAE,8BACnBkC,OAAON,EAAWG,EAAYC,GAC5BG,EAAUnC,EAAE,iDACd8B,SAAU3B,KAAKO,SAAS0B,KAAK,WAAajC,KAAKuB,SAAWvB,KAAKwB,UAAU,QACzEG,SAASV,GAAMU,SAAS3B,KAAKI,QAAQc,MAEvClB,MAAKO,SAAS2B,KAAKF,GACnBnC,EAAEW,OAAOR,MACRgC,QAAShC,KAAKO,SAAS4B,SACvBV,UAAWA,EACXG,WAAYA,EACZE,aAAcA,IAEf9B,KAAKgC,QAAQD,OAAOD,EAEpB,IAAIX,GAAQnB,KAAKI,QAAQe,OAASiB,KAAKC,IAAIZ,EAAUa,aAAcV,EAAWU,cAAeT,EAAcS,aAAa,EACpHlB,EAASpB,KAAKI,QAAQgB,QAAUgB,KAAKC,IAAIZ,EAAUc,cAAeX,EAAWW,cACjFd,GAAUE,SAAS,aACnBC,EAAWD,SAAS,cACpB3B,KAAKgC,QAAQQ,KAAMrB,MAAOA,EAAOC,OAAQA,IACrCpB,KAAKI,QAAQgB,SAChBK,EAAUe,IAAI,cAAef,EAAUL,SAAW,MAClDQ,EAAWY,IAAI,cAAeZ,EAAWR,SAAW,OAErDpB,KAAKyC,QAAO,GACZzC,KAAK0C,SAAQ,IAGdrC,EAAOgB,UAAUsB,OAAS,WACrB3C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKc,MACnCd,KAAKa,MAGXR,EAAOgB,UAAUR,GAAK,SAAU+B,GAC/B,MAAI5C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQa,YAAY7C,KAAKwB,UAAY,QAAQG,SAAS3B,KAAKuB,UAChEvB,KAAKO,SAAS0B,KAAK,WAAW,QACzBW,GAAQ5C,KAAK0C,aAGnBrC,EAAOgB,UAAUP,IAAM,SAAU8B,GAChC,MAAI5C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQa,YAAY7C,KAAKuB,UAAUI,SAAS3B,KAAKwB,UAAY,QAClExB,KAAKO,SAAS0B,KAAK,WAAW,QACzBW,GAAQ5C,KAAK0C,aAGnBrC,EAAOgB,UAAUyB,OAAS,WACzB9C,KAAKgC,QAAQe,WAAW,YACxB/C,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAU2B,QAAU,WAC1BhD,KAAKgC,QAAQV,KAAK,WAAY,YAC9BtB,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAUoB,OAAS,SAAUG,GAC/B5C,KAAKO,SAAS0B,KAAK,YAAajC,KAAKgD,UACpChD,KAAK8C,SACN9C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKa,GAAG+B,GACtC5C,KAAKc,IAAI8B,IAGfvC,EAAOgB,UAAUqB,QAAU,SAAUE,GACpC5C,KAAKO,SAASO,IAAI,oBACb8B,GAAQ5C,KAAKO,SAAS0C,SAC3BjD,KAAKO,SAASM,GAAG,mBAAoBhB,EAAEqD,MAAM,WAC5ClD,KAAKyC,UACHzC,QAGJK,EAAOgB,UAAU8B,QAAU,WAC1BnD,KAAKO,SAASO,IAAI,oBAClBd,KAAK8B,aAAasB,SAClBpD,KAAKO,SAAS8C,WAAW,aACzBrD,KAAKO,SAAS+C,SAiBf,IAAIC,GAAM1D,EAAE2D,GAAGC,eAEf5D,GAAE2D,GAAGC,gBAA8B3D,EACnCD,EAAE2D,GAAGC,gBAAgBC,YAAcrD,EAKnCR,EAAE2D,GAAGb,OAAOgB,WAAa,WAExB,MADA9D,GAAE2D,GAAGC,gBAAkBF,EAChBvD,MAMRH,EAAE,WACDA,EAAE,6CAA6C4D,oBAGhD5D,EAAE+D,UAAU/C,GAAG,kBAAmB,2BAA4B,SAASgD,GACtE,GAAIC,GAAYjE,EAAEG,MAAM+D,KAAK,uBAC7BD,GAAUL,gBAAgB,UAC1BI,EAAEG,oBAGFC"}

View File

@ -0,0 +1,180 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap2-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function ($) {
'use strict';
// TOGGLE PUBLIC CLASS DEFINITION
// ==============================
var Toggle = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, this.defaults(), options)
this.render()
}
Toggle.VERSION = '2.2.0'
Toggle.DEFAULTS = {
on: 'On',
off: 'Off',
onstyle: 'primary',
offstyle: 'default',
size: 'normal',
style: '',
width: null,
height: null
}
Toggle.prototype.defaults = function() {
return {
on: this.$element.attr('data-on') || Toggle.DEFAULTS.on,
off: this.$element.attr('data-off') || Toggle.DEFAULTS.off,
onstyle: this.$element.attr('data-onstyle') || Toggle.DEFAULTS.onstyle,
offstyle: this.$element.attr('data-offstyle') || Toggle.DEFAULTS.offstyle,
size: this.$element.attr('data-size') || Toggle.DEFAULTS.size,
style: this.$element.attr('data-style') || Toggle.DEFAULTS.style,
width: this.$element.attr('data-width') || Toggle.DEFAULTS.width,
height: this.$element.attr('data-height') || Toggle.DEFAULTS.height
}
}
Toggle.prototype.render = function () {
this._onstyle = 'btn-' + this.options.onstyle
this._offstyle = 'btn-' + this.options.offstyle
var size = this.options.size === 'large' ? 'btn-large'
: this.options.size === 'small' ? 'btn-small'
: this.options.size === 'mini' ? 'btn-mini'
: ''
var $toggleOn = $('<label class="btn">').html(this.options.on)
.addClass(this._onstyle + ' ' + size)
var $toggleOff = $('<label class="btn">').html(this.options.off)
.addClass(this._offstyle + ' ' + size + ' active')
var $toggleHandle = $('<span class="toggle-handle btn btn-default">')
.addClass(size)
var $toggleGroup = $('<div class="toggle-group">')
.append($toggleOn, $toggleOff, $toggleHandle)
var $toggle = $('<div class="toggle btn" data-toggle="toggle">')
.addClass( this.$element.prop('checked') ? this._onstyle : this._offstyle+' off' )
.addClass(size).addClass(this.options.style)
this.$element.wrap($toggle)
$.extend(this, {
$toggle: this.$element.parent(),
$toggleOn: $toggleOn,
$toggleOff: $toggleOff,
$toggleGroup: $toggleGroup
})
this.$toggle.append($toggleGroup)
var width = this.options.width || Math.max($toggleOn.width(), $toggleOff.width())+($toggleHandle.outerWidth()/2)
var height = this.options.height || Math.max($toggleOn.height(), $toggleOff.height())
$toggleOn.addClass('toggle-on')
$toggleOff.addClass('toggle-off')
this.$toggle.css({ width: width, height: height })
if (this.options.height) {
$toggleOn.css('line-height', $toggleOn.height() + 'px')
$toggleOff.css('line-height', $toggleOff.height() + 'px')
}
this.update(true)
this.trigger(true)
}
Toggle.prototype.toggle = function () {
if (this.$element.prop('checked')) this.off()
else this.on()
}
Toggle.prototype.on = function (silent) {
if (this.$element.prop('disabled')) return false
this.$toggle.removeClass(this._offstyle + ' off').addClass(this._onstyle)
this.$element.prop('checked', true)
if (!silent) this.trigger()
}
Toggle.prototype.off = function (silent) {
if (this.$element.prop('disabled')) return false
this.$toggle.removeClass(this._onstyle).addClass(this._offstyle + ' off')
this.$element.prop('checked', false)
if (!silent) this.trigger()
}
Toggle.prototype.enable = function () {
this.$toggle.removeAttr('disabled')
this.$element.prop('disabled', false)
}
Toggle.prototype.disable = function () {
this.$toggle.attr('disabled', 'disabled')
this.$element.prop('disabled', true)
}
Toggle.prototype.update = function (silent) {
if (this.$element.prop('disabled')) this.disable()
else this.enable()
if (this.$element.prop('checked')) this.on(silent)
else this.off(silent)
}
Toggle.prototype.trigger = function (silent) {
this.$element.off('change.bs.toggle')
if (!silent) this.$element.change()
this.$element.on('change.bs.toggle', $.proxy(function() {
this.update()
}, this))
}
Toggle.prototype.destroy = function() {
this.$element.off('change.bs.toggle')
this.$toggleGroup.remove()
this.$element.removeData('bs.toggle')
this.$element.unwrap()
}
// TOGGLE PLUGIN DEFINITION
// ========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.toggle')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.toggle', (data = new Toggle(this, options)))
if (typeof option == 'string' && data[option]) data[option]()
})
}
var old = $.fn.bootstrapToggle
$.fn.bootstrapToggle = Plugin
$.fn.bootstrapToggle.Constructor = Toggle
// TOGGLE NO CONFLICT
// ==================
$.fn.toggle.noConflict = function () {
$.fn.bootstrapToggle = old
return this
}
// TOGGLE DATA-API
// ===============
$(function() {
$('input[type=checkbox][data-toggle^=toggle]').bootstrapToggle()
})
$(document).on('click.bs.toggle', 'div[data-toggle^=toggle]', function(e) {
var $checkbox = $(this).find('input[type=checkbox]')
$checkbox.bootstrapToggle('toggle')
e.preventDefault()
})
}(jQuery);

View File

@ -0,0 +1,9 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap2-toggle.js v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-large":"small"===this.options.size?"btn-small":"mini"===this.options.size?"btn-mini":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b+" active"),e=a('<span class="toggle-handle btn btn-default">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.width(),d.width())+e.outerWidth()/2,i=this.options.height||Math.max(c.height(),d.height());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),void(a||this.trigger()))},c.prototype.off=function(a){return this.$element.prop("disabled")?!1:(this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),void(a||this.trigger()))},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){var c=a(this).find("input[type=checkbox]");c.bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
//# sourceMappingURL=bootstrap2-toggle.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"bootstrap2-toggle.min.js","sources":["bootstrap2-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":";;;;;;;CASE,SAAUA,GACV,YAoID,SAASC,GAAOC,GACf,MAAOC,MAAKC,KAAK,WAChB,GAAIC,GAAUL,EAAEG,MACZG,EAAUD,EAAMC,KAAK,aACrBC,EAA2B,gBAAVL,IAAsBA,CAEtCI,IAAMD,EAAMC,KAAK,YAAcA,EAAO,GAAIE,GAAOL,KAAMI,IACvC,gBAAVL,IAAsBI,EAAKJ,IAASI,EAAKJ,OAtItD,GAAIM,GAAS,SAAUC,EAASF,GAC/BJ,KAAKO,SAAYV,EAAES,GACnBN,KAAKI,QAAYP,EAAEW,UAAWR,KAAKS,WAAYL,GAC/CJ,KAAKU,SAGNL,GAAOM,QAAW,QAElBN,EAAOO,UACNC,GAAI,KACJC,IAAK,MACLC,QAAS,UACTC,SAAU,UACVC,KAAM,SACNC,MAAO,GACPC,MAAO,KACPC,OAAQ,MAGTf,EAAOgB,UAAUZ,SAAW,WAC3B,OACCI,GAAIb,KAAKO,SAASe,KAAK,YAAcjB,EAAOO,SAASC,GACrDC,IAAKd,KAAKO,SAASe,KAAK,aAAejB,EAAOO,SAASE,IACvDC,QAASf,KAAKO,SAASe,KAAK,iBAAmBjB,EAAOO,SAASG,QAC/DC,SAAUhB,KAAKO,SAASe,KAAK,kBAAoBjB,EAAOO,SAASI,SACjEC,KAAMjB,KAAKO,SAASe,KAAK,cAAgBjB,EAAOO,SAASK,KACzDC,MAAOlB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASM,MAC3DC,MAAOnB,KAAKO,SAASe,KAAK,eAAiBjB,EAAOO,SAASO,MAC3DC,OAAQpB,KAAKO,SAASe,KAAK,gBAAkBjB,EAAOO,SAASQ,SAI/Df,EAAOgB,UAAUX,OAAS,WACzBV,KAAKuB,SAAW,OAASvB,KAAKI,QAAQW,QACtCf,KAAKwB,UAAY,OAASxB,KAAKI,QAAQY,QACvC,IAAIC,GAA6B,UAAtBjB,KAAKI,QAAQa,KAAmB,YAClB,UAAtBjB,KAAKI,QAAQa,KAAmB,YACV,SAAtBjB,KAAKI,QAAQa,KAAkB,WAC/B,GACCQ,EAAY5B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQS,IACzDc,SAAS3B,KAAKuB,SAAW,IAAMN,GAC7BW,EAAa/B,EAAE,uBAAuB6B,KAAK1B,KAAKI,QAAQU,KAC1Da,SAAS3B,KAAKwB,UAAY,IAAMP,EAAO,WACrCY,EAAgBhC,EAAE,gDACpB8B,SAASV,GACPa,EAAejC,EAAE,8BACnBkC,OAAON,EAAWG,EAAYC,GAC5BG,EAAUnC,EAAE,iDACd8B,SAAU3B,KAAKO,SAAS0B,KAAK,WAAajC,KAAKuB,SAAWvB,KAAKwB,UAAU,QACzEG,SAASV,GAAMU,SAAS3B,KAAKI,QAAQc,MAEvClB,MAAKO,SAAS2B,KAAKF,GACnBnC,EAAEW,OAAOR,MACRgC,QAAShC,KAAKO,SAAS4B,SACvBV,UAAWA,EACXG,WAAYA,EACZE,aAAcA,IAEf9B,KAAKgC,QAAQD,OAAOD,EAEpB,IAAIX,GAAQnB,KAAKI,QAAQe,OAASiB,KAAKC,IAAIZ,EAAUN,QAASS,EAAWT,SAAUU,EAAcS,aAAa,EAC1GlB,EAASpB,KAAKI,QAAQgB,QAAUgB,KAAKC,IAAIZ,EAAUL,SAAUQ,EAAWR,SAC5EK,GAAUE,SAAS,aACnBC,EAAWD,SAAS,cACpB3B,KAAKgC,QAAQO,KAAMpB,MAAOA,EAAOC,OAAQA,IACrCpB,KAAKI,QAAQgB,SAChBK,EAAUc,IAAI,cAAed,EAAUL,SAAW,MAClDQ,EAAWW,IAAI,cAAeX,EAAWR,SAAW,OAErDpB,KAAKwC,QAAO,GACZxC,KAAKyC,SAAQ,IAGdpC,EAAOgB,UAAUqB,OAAS,WACrB1C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKc,MACnCd,KAAKa,MAGXR,EAAOgB,UAAUR,GAAK,SAAU8B,GAC/B,MAAI3C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQY,YAAY5C,KAAKwB,UAAY,QAAQG,SAAS3B,KAAKuB,UAChEvB,KAAKO,SAAS0B,KAAK,WAAW,QACzBU,GAAQ3C,KAAKyC,aAGnBpC,EAAOgB,UAAUP,IAAM,SAAU6B,GAChC,MAAI3C,MAAKO,SAAS0B,KAAK,aAAoB,GAC3CjC,KAAKgC,QAAQY,YAAY5C,KAAKuB,UAAUI,SAAS3B,KAAKwB,UAAY,QAClExB,KAAKO,SAAS0B,KAAK,WAAW,QACzBU,GAAQ3C,KAAKyC,aAGnBpC,EAAOgB,UAAUwB,OAAS,WACzB7C,KAAKgC,QAAQc,WAAW,YACxB9C,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAU0B,QAAU,WAC1B/C,KAAKgC,QAAQV,KAAK,WAAY,YAC9BtB,KAAKO,SAAS0B,KAAK,YAAY,IAGhC5B,EAAOgB,UAAUmB,OAAS,SAAUG,GAC/B3C,KAAKO,SAAS0B,KAAK,YAAajC,KAAK+C,UACpC/C,KAAK6C,SACN7C,KAAKO,SAAS0B,KAAK,WAAYjC,KAAKa,GAAG8B,GACtC3C,KAAKc,IAAI6B,IAGftC,EAAOgB,UAAUoB,QAAU,SAAUE,GACpC3C,KAAKO,SAASO,IAAI,oBACb6B,GAAQ3C,KAAKO,SAASyC,SAC3BhD,KAAKO,SAASM,GAAG,mBAAoBhB,EAAEoD,MAAM,WAC5CjD,KAAKwC,UACHxC,QAGJK,EAAOgB,UAAU6B,QAAU,WAC1BlD,KAAKO,SAASO,IAAI,oBAClBd,KAAK8B,aAAaqB,SAClBnD,KAAKO,SAAS6C,WAAW,aACzBpD,KAAKO,SAAS8C,SAiBf,IAAIC,GAAMzD,EAAE0D,GAAGC,eAEf3D,GAAE0D,GAAGC,gBAA8B1D,EACnCD,EAAE0D,GAAGC,gBAAgBC,YAAcpD,EAKnCR,EAAE0D,GAAGb,OAAOgB,WAAa,WAExB,MADA7D,GAAE0D,GAAGC,gBAAkBF,EAChBtD,MAMRH,EAAE,WACDA,EAAE,6CAA6C2D,oBAGhD3D,EAAE8D,UAAU9C,GAAG,kBAAmB,2BAA4B,SAAS+C,GACtE,GAAIC,GAAYhE,EAAEG,MAAM8D,KAAK,uBAC7BD,GAAUL,gBAAgB,UAC1BI,EAAEG,oBAGFC"}

View File

@ -8,6 +8,7 @@
@import "jquery.mloading";
@import "jquery-confirm.min";
@import "bootstrap-datetimepicker.min";
@import "bootstrap/bootstrap-toggle.min";
@import "codemirror/lib/codemirror";
@import "editormd/css/editormd.min";
@ -203,4 +204,14 @@ input.form-control {
color: #23272B;
font-size: 1rem;
}
}
.table th, .table td {
padding: 0.75rem 0.1rem;
vertical-align: top;
border-top: 1px solid #dee2e6;
}
.table .thead-light th{
white-space: nowrap;
}

View File

@ -0,0 +1,83 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap-toggle.css v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
.checkbox label .toggle,
.checkbox-inline .toggle {
margin-left: -20px;
margin-right: 5px;
}
.toggle {
position: relative;
overflow: hidden;
}
.toggle input[type="checkbox"] {
display: none;
}
.toggle-group {
position: absolute;
width: 200%;
top: 0;
bottom: 0;
left: 0;
transition: left 0.35s;
-webkit-transition: left 0.35s;
-moz-user-select: none;
-webkit-user-select: none;
}
.toggle.off .toggle-group {
left: -100%;
}
.toggle-on {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 50%;
margin: 0;
border: 0;
border-radius: 0;
}
.toggle-off {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
right: 0;
margin: 0;
border: 0;
border-radius: 0;
}
.toggle-handle {
position: relative;
margin: 0 auto;
padding-top: 0px;
padding-bottom: 0px;
height: 100%;
width: 0px;
border-width: 0 1px;
}
.toggle.btn { min-width: 59px; min-height: 34px; }
.toggle-on.btn { padding-right: 24px; }
.toggle-off.btn { padding-left: 24px; }
.toggle.btn-lg { min-width: 79px; min-height: 45px; }
.toggle-on.btn-lg { padding-right: 31px; }
.toggle-off.btn-lg { padding-left: 31px; }
.toggle-handle.btn-lg { width: 40px; }
.toggle.btn-sm { min-width: 50px; min-height: 30px;}
.toggle-on.btn-sm { padding-right: 20px; }
.toggle-off.btn-sm { padding-left: 20px; }
.toggle.btn-xs { min-width: 35px; min-height: 22px;}
.toggle-on.btn-xs { padding-right: 12px; }
.toggle-off.btn-xs { padding-left: 12px; }

View File

@ -0,0 +1,28 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap-toggle.css v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px}
.toggle{position:relative;overflow:hidden}
.toggle input[type=checkbox]{display:none}
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
.toggle.off .toggle-group{left:-100%}
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
.toggle.btn{min-width:59px;min-height:34px}
.toggle-on.btn{padding-right:24px}
.toggle-off.btn{padding-left:24px}
.toggle.btn-lg{min-width:79px;min-height:45px}
.toggle-on.btn-lg{padding-right:31px}
.toggle-off.btn-lg{padding-left:31px}
.toggle-handle.btn-lg{width:40px}
.toggle.btn-sm{min-width:50px;min-height:30px}
.toggle-on.btn-sm{padding-right:20px}
.toggle-off.btn-sm{padding-left:20px}
.toggle.btn-xs{min-width:35px;min-height:22px}
.toggle-on.btn-xs{padding-right:12px}
.toggle-off.btn-xs{padding-left:12px}

View File

@ -0,0 +1,85 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap2-toggle.css v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
label.checkbox .toggle,
label.checkbox.inline .toggle {
margin-left: -20px;
margin-right: 5px;
}
.toggle {
min-width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.toggle input[type="checkbox"] {
display: none;
}
.toggle-group {
position: absolute;
width: 200%;
top: 0;
bottom: 0;
left: 0;
transition: left 0.35s;
-webkit-transition: left 0.35s;
-moz-user-select: none;
-webkit-user-select: none;
}
.toggle.off .toggle-group {
left: -100%;
}
.toggle-on {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 50%;
margin: 0;
border: 0;
border-radius: 0;
}
.toggle-off {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
right: 0;
margin: 0;
border: 0;
border-radius: 0;
}
.toggle-handle {
position: relative;
margin: 0 auto;
padding-top: 0px;
padding-bottom: 0px;
height: 100%;
width: 0px;
border-width: 0 1px;
}
.toggle-handle.btn-mini {
top: -1px;
}
.toggle.btn { min-width: 30px; }
.toggle-on.btn { padding-right: 24px; }
.toggle-off.btn { padding-left: 24px; }
.toggle.btn-large { min-width: 40px; }
.toggle-on.btn-large { padding-right: 35px; }
.toggle-off.btn-large { padding-left: 35px; }
.toggle.btn-small { min-width: 25px; }
.toggle-on.btn-small { padding-right: 20px; }
.toggle-off.btn-small { padding-left: 20px; }
.toggle.btn-mini { min-width: 20px; }
.toggle-on.btn-mini { padding-right: 12px; }
.toggle-off.btn-mini { padding-left: 12px; }

View File

@ -0,0 +1,28 @@
/*! ========================================================================
* Bootstrap Toggle: bootstrap2-toggle.css v2.2.0
* http://www.bootstraptoggle.com
* ========================================================================
* Copyright 2014 Min Hur, The New York Times Company
* Licensed under MIT
* ======================================================================== */
label.checkbox .toggle,label.checkbox.inline .toggle{margin-left:-20px;margin-right:5px}
.toggle{min-width:40px;height:20px;position:relative;overflow:hidden}
.toggle input[type=checkbox]{display:none}
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
.toggle.off .toggle-group{left:-100%}
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0}
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px}
.toggle-handle.btn-mini{top:-1px}
.toggle.btn{min-width:30px}
.toggle-on.btn{padding-right:24px}
.toggle-off.btn{padding-left:24px}
.toggle.btn-large{min-width:40px}
.toggle-on.btn-large{padding-right:35px}
.toggle-off.btn-large{padding-left:35px}
.toggle.btn-small{min-width:25px}
.toggle-on.btn-small{padding-right:20px}
.toggle-off.btn-small{padding-left:20px}
.toggle.btn-mini{min-width:20px}
.toggle-on.btn-mini{padding-right:12px}
.toggle-off.btn-mini{padding-left:12px}

View File

@ -111,7 +111,9 @@ class AccountsController < ApplicationController
sync_params = {
password: params[:password].to_s,
email: @user.mail
email: @user.mail,
login_name: @user.login,
source_id: 0
}
interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)

View File

@ -1,9 +1,9 @@
class Admins::DashboardsController < Admins::BaseController
def index
# 用户活跃数
day_user_ids = CommitLog.where(created_at: today).pluck(:project_id).uniq
weekly_user_ids = CommitLog.where(created_at: current_week).pluck(:project_id).uniq
month_user_ids = CommitLog.where(created_at: current_month).pluck(:project_id).uniq
day_user_ids = CommitLog.where(created_at: today).pluck(:user_id).uniq
weekly_user_ids = CommitLog.where(created_at: current_week).pluck(:user_id).uniq
month_user_ids = CommitLog.where(created_at: current_month).pluck(:user_id).uniq
@active_user_count = User.where(last_login_on: today).or(User.where(id: day_user_ids)).count
@weekly_active_user_count = User.where(last_login_on: current_week).or(User.where(id: weekly_user_ids)).count
@month_active_user_count = User.where(last_login_on: current_month).or(User.where(id: month_user_ids)).count

View File

@ -7,12 +7,12 @@ class Admins::MessageTemplatesController < Admins::BaseController
end
def new
@message_template = MessageTemplate::CustomTip.new
@message_template = MessageTemplate.new
end
def create
@message_template = MessageTemplate::CustomTip.new(ignore_params)
def create
@message_template = MessageTemplate::CustomTip.new(message_template_params)
@message_template.type = "MessageTemplate::CustomTip"
if @message_template.save!
redirect_to admins_message_templates_path
flash[:success] = "创建消息模板成功"
@ -47,7 +47,9 @@ class Admins::MessageTemplatesController < Admins::BaseController
private
def message_template_params
params.require(@message_template.type.split("::").join("_").underscore.to_sym).permit!
# type = @message_template.present? ? @message_template.type : "MessageTemplate::CustomTip"
# params.require(type.split("::").join("_").underscore.to_sym).permit!
params.require(:message_template).permit!
end
def get_template

View File

@ -0,0 +1,26 @@
class Admins::NpsController < Admins::BaseController
def index
@on_off_switch = EduSetting.get("nps-on-off-switch").to_s == 'true'
@user_nps = UserNp.joins(:user).order(created_at: :desc)
keyword = params[:keyword].to_s.strip.presence
if keyword
sql = 'CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword'
@user_nps = @user_nps.where(sql, keyword: "%#{keyword}%")
end
@user_nps = @user_nps.where("action_type != 'close'") if params[:done_score].present?
@min_score = @user_nps.where("action_type != 'close'").minimum("score")
@max_score = @user_nps.where("action_type != 'close'").maximum("score")
@score_total_count = UserNp.where("action_type !='close'").count
@user_nps = paginate @user_nps.includes(:user)
end
def switch_change
edu_setting = EduSetting.find_by(name: "nps-on-off-switch")
if edu_setting.blank?
edu_setting = EduSetting.new(name: "nps-on-off-switch")
end
edu_setting.value = params[:switch].to_s
edu_setting.save
render_ok
end
end

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ class Api::V1::BaseController < ApplicationController
include Api::ProjectHelper
include Api::UserHelper
include Api::PullHelper
# before_action :doorkeeper_authorize!
# skip_before_action :user_setup
@ -30,18 +31,25 @@ class Api::V1::BaseController < ApplicationController
# 具有对仓库的管理权限
def require_manager_above
@project = load_project
return render_forbidden unless current_user.admin? && @project.manager?(current_user)
return render_forbidden if !current_user.admin? && !@project.manager?(current_user)
end
# 具有对仓库的操作权限
def require_operate_above
@project = load_project
return render_forbidden unless current_user.admin? && @project.operator?(current_user)
return render_forbidden if !current_user.admin? && !@project.operator?(current_user)
end
# 具有仓库的操作权限或者fork仓库的操作权限
def require_operate_above_or_fork_project
@project = load_project
puts !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
return render_forbidden if !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
end
# 具有对仓库的访问权限
def require_public_and_member_above
@project = load_project
return render_forbidden unless @project.is_public || (current_user.admin? && @project.member?(current_user))
return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user)
end
end

View File

@ -1,9 +1,13 @@
class Api::V1::Projects::ContentsController < Api::V1::BaseController
before_action :require_operate_above, only: [:batch]
before_action :require_operate_above_or_fork_project, only: [:batch]
def batch
@result_object = Api::V1::Projects::Contents::BatchCreateService.call(@project, batch_content_params, current_user&.gitea_token)
puts @result_object
@batch_content_params = batch_content_params
# 处理下author和committer信息如果没传则默认为当前用户信息
@batch_content_params.merge!(author_email: current_user.mail, author_name: current_user.login) if batch_content_params[:author_email].blank? && batch_content_params[:author_name].blank?
@batch_content_params.merge!(committer_email: current_user.mail, committer_name: current_user.login) if batch_content_params[:committer_email].blank? && batch_content_params[:committer_name].blank?
@result_object = Api::V1::Projects::Contents::BatchCreateService.call(@project, @batch_content_params, @project.owner.gitea_token)
end
private

View File

@ -0,0 +1,5 @@
class Api::V1::Projects::Pulls::BaseController < Api::V1::BaseController
before_action :require_public_and_member_above
before_action :load_pull_request
end

View File

@ -0,0 +1,40 @@
class Api::V1::Projects::Pulls::JournalsController < Api::V1::Projects::Pulls::BaseController
def index
@journals = Api::V1::Projects::Pulls::Journals::ListService.call(@project, @pull_request, params, current_user)
@journals = @journals.limit(200)
end
def create
@journal = Api::V1::Projects::Pulls::Journals::CreateService.call(@project, @pull_request, create_params, current_user)
end
before_action :find_journal, only: [:update, :destroy]
def update
@journal = Api::V1::Projects::Pulls::Journals::UpdateService.call(@project, @pull_request, @journal, update_params, current_user)
end
def destroy
if @journal.destroy
render_ok
else
render_error("删除评论失败!")
end
end
private
def create_params
params.permit(:parent_id, :line_code, :note, :commit_id, :path, :type, :review_id, :diff => {})
end
def update_params
params.permit(:note, :commit_id, :state)
end
def find_journal
@journal = @pull_request.journals.find_by_id(params[:id])
return render_not_found unless @journal.present?
end
end

View File

@ -0,0 +1,20 @@
class Api::V1::Projects::Pulls::PullsController < Api::V1::BaseController
before_action :require_public_and_member_above
def index
@pulls = Api::V1::Projects::Pulls::ListService.call(@project, query_params)
@pulls = kaminari_paginate(@pulls)
end
before_action :load_pull_request, only: [:show]
def show
@result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token)
@last_review = @pull_request.reviews.order(created_at: :desc).take
end
private
def query_params
params.permit(:status, :keyword, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction)
end
end

View File

@ -0,0 +1,23 @@
class Api::V1::Projects::Pulls::ReviewsController < Api::V1::Projects::Pulls::BaseController
def index
@reviews = @pull_request.reviews
@reviews = @reviews.where(status: params[:status]) if params[:status].present?
# @reviews = kaminari_paginate(@reviews)
end
before_action :require_reviewer, only: [:create]
def create
@review = Api::V1::Projects::Pulls::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
end
private
def require_reviewer
return render_forbidden('您没有审查权限,请联系项目管理员') if !current_user.admin? && !@pull_request.reviewers.exists?(current_user.id) && !@project.manager?(current_user)
end
def review_params
params.require(:review).permit(:content, :commit_id, :status)
end
end

View File

@ -0,0 +1,10 @@
class Api::V1::Projects::Pulls::VersionsController < Api::V1::Projects::Pulls::BaseController
def index
@result_object = Api::V1::Projects::Pulls::Versions::ListService.call(@project, @pull_request, {page: page, limit: limit}, current_user&.gitea_token)
end
def diff
@result_object = Api::V1::Projects::Pulls::Versions::GetDiffService.call(@project, @pull_request, params[:id], {filepath: params[:filepath]}, current_user&.gitea_token)
end
end

View File

@ -5,11 +5,13 @@ class Api::V1::Projects::WebhooksController < Api::V1::BaseController
def index
# @result_object = Api::V1::Projects::Webhooks::ListService.call(@project, current_user&.gitea_token)
@webhooks = @project.webhooks
@webhooks = @webhooks.where(type: params[:type]) if params[:type].present?
@webhooks = kaminari_paginate(@webhooks)
end
def create
@result_object = Api::V1::Projects::Webhooks::CreateService.call(@project, webhook_params, current_user&.gitea_token)
return render_error("webhooks数量已到上限请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 49
@result_object = Api::V1::Projects::Webhooks::CreateService.call(@project, create_webhook_params, current_user&.gitea_token)
end
def show
@ -44,6 +46,10 @@ class Api::V1::Projects::WebhooksController < Api::V1::BaseController
end
private
def create_webhook_params
params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, :type, events: [])
end
def webhook_params
params.require(:webhook).permit(:active, :branch_filter, :http_method, :url, :content_type, :secret, events: [])
end

View File

@ -15,6 +15,5 @@ class Api::V1::ProjectsController < Api::V1::BaseController
def blame
@result_object = Api::V1::Projects::BlameService.call(@project, params[:sha], params[:filepath], current_user&.gitea_token)
puts @result_object
end
end

View File

@ -1,6 +1,81 @@
class Api::V1::UsersController < Api::V1::BaseController
def index
render_ok
before_action :load_observe_user
before_action :check_auth_for_observe_user
def send_email_vefify_code
code = %W(0 1 2 3 4 5 6 7 8 9)
verification_code = code.sample(6).join
mail = params[:email]
code_type = params[:code_type]
sign = Digest::MD5.hexdigest("#{OPENKEY}#{mail}")
Rails.logger.info sign
tip_exception(501, "请求不合理") if sign != params[:smscode]
# 60s内不能重复发送
send_email_limit_cache_key = "send_email_60_second_limit:#{mail}"
tip_exception(-2, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key)
send_email_control = LimitForbidControl::SendEmailCode.new(mail)
tip_exception(-2, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin
UserMailer.update_email(mail, verification_code).deliver_now
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment!
rescue Exception => e
logger_error(e)
tip_exception(-2,"邮件发送失败,请稍后重试")
end
ver_params = {code_type: code_type, code: verification_code, email: mail}
last_code = VerificationCode.where(code_type: code_type, email: mail).last
last_code.update_attributes!({created_at: Time.current - 10.minute}) if last_code.present?
data = VerificationCode.new(ver_params)
if data.save!
render_ok
else
tip_exception(-1, "创建数据失败")
end
end
def check_password
password = params[:password]
return tip_exception(-5, "8~16位密码支持字母数字和符号") unless password =~ CustomRegexp::PASSWORD
return tip_exception(-5, "密码错误") unless @observe_user.check_password?(password)
render_ok
end
def check_email
mail = strip(params[:email])
return tip_exception(-2, "邮件格式有误") unless mail =~ CustomRegexp::EMAIL
exist_owner = Owner.find_by(mail: mail)
return tip_exception(-2, '邮箱已被使用') if exist_owner
render_ok
end
def check_email_verify_code
code = strip(params[:code])
mail = strip(params[:email])
code_type = params[:code_type]
return tip_exception(-2, "邮件格式有误") unless mail =~ CustomRegexp::EMAIL
verifi_code = VerificationCode.where(email: mail, code: code, code_type: code_type).last
return render_ok if code == "123123" && EduSetting.get("code_debug") # 万能验证码,用于测试 # TODO 万能验证码,用于测试
return tip_exception(-6, "验证码不正确") if verifi_code&.code != code
return tip_exception(-6, "验证码已失效") if !verifi_code&.effective?
render_ok
end
def update_email
@result_object = Api::V1::Users::UpdateEmailService.call(@observe_user, params, current_user.gitea_token)
if @result_object
return render_ok
else
return render_error('更改邮箱失败!')
end
end
end

View File

@ -20,9 +20,9 @@ class ApplicationController < ActionController::Base
# TODO
# check sql query time
before_action do
if request.subdomain === 'testforgeplus' || request.subdomain === "profiler"
Rack::MiniProfiler.authorize_request
end
# if request.subdomain === 'testforgeplus' || request.subdomain === "profiler"
# Rack::MiniProfiler.authorize_request
# end
end
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
@ -173,6 +173,25 @@ class ApplicationController < ActionController::Base
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
def require_login_or_token
if params[:token].present?
user = User.try_to_autologin(params[:token])
User.current = user
end
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
def require_login_cloud_ide_saas
if params[:sign].present? && params[:email].present?
sign = Digest::MD5.hexdigest("#{OPENKEY}#{params[:email]}")
if params[:sign].to_s == sign
user = User.find_by(mail: params[:email])
User.current = user
end
end
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
def require_profile_completed
tip_exception(411, "请完善资料后再操作") unless User.current.profile_is_completed?
end
@ -277,11 +296,11 @@ class ApplicationController < ActionController::Base
end
end
# if !User.current.logged? && Rails.env.development?
# user = User.find 1
# User.current = user
# start_user_session(user)
# end
if !User.current.logged? && Rails.env.development?
user = User.find 1
User.current = user
start_user_session(user)
end
# 测试版前端需求
@ -619,7 +638,7 @@ class ApplicationController < ActionController::Base
def kaminari_paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i
limit = (limit.to_i.zero? || limit.to_i > 25) ? 25 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
relation.page(page).per(limit)
@ -704,9 +723,15 @@ class ApplicationController < ActionController::Base
# @project = nil if !@project.is_public?
# render_forbidden and return
else
logger.info "###########project not found"
@project = nil
render_not_found and return
if @project.present?
logger.info "########### has project and but can't read project"
@project = nil
render_forbidden and return
else
logger.info "###########project not found"
@project = nil
render_not_found and return
end
end
@project
end

View File

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

View File

@ -15,12 +15,15 @@ class CommitLogsController < ApplicationController
owner = User.find_by(login: owner_name)
project = Project.where(identifier: repository_name).where(user_id: owner&.id)&.first
project = Project.where(identifier: repository_name).where(gpid: repository_id)&.first if project.blank?
project.update_column(:updated_on, Time.now) if project.present?
params[:commits].each do |commit|
commit_id = commit[:id]
message = commit[:message]
CommitLog.create(user: user, project: project, repository_id: repository_id,
name: repository_name, full_name: repository_full_name,
ref: ref, commit_id: commit_id, message: message)
# 统计数据新增
CacheAsyncSetJob.perform_later("project_common_service", {commits: 1}, project.id)
end
end

View File

@ -0,0 +1,19 @@
module Api::PullHelper
extend ActiveSupport::Concern
def load_pull_request
pull_request_id = params[:pull_id] || params[:id]
@pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || PullRequest.find_by_id(pull_request_id)
@issue = @pull_request&.issue
if @pull_request
logger.info "###########pull_request founded"
@pull_request
else
logger.info "###########pull_request not found"
@pull_request = nil
render_not_found and return
end
@pull_request
end
end

View File

@ -16,4 +16,13 @@ module Api::UserHelper
end
@observe_user
end
# 是否具有查看用户或编辑用户的权限
def check_auth_for_observe_user
return render_forbidden unless current_user.admin? || @observe_user.id == current_user.id
end
def strip(str)
str.to_s.strip.presence
end
end

View File

@ -16,6 +16,7 @@ module RegisterHelper
return unless user.valid?
interactor = Gitea::RegisterInteractor.call({username: username, email: email, password: password})
result ={}
if interactor.success?
gitea_user = interactor.result
result = Gitea::User::GenerateTokenService.call(username, password)
@ -26,7 +27,7 @@ module RegisterHelper
result[:user] = {id: user.id, token: user.gitea_token}
end
else
result[:message] = interactor.error
result[:message] = interactor.result[:message]
end
result
end

View File

@ -3,7 +3,7 @@ module RenderHelper
render json: { status: 0, message: 'success' }.merge(data)
end
def render_error(message = '')
def render_error(message = '', status = -1)
render json: { status: status, message: message }
end

View File

@ -11,6 +11,7 @@ module Repository::LanguagesPercentagable
end
def update_project_language(language)
return if @project.project_language.present?
db_language = ProjectLanguage.find_or_create_by!(name: language.keys.first.downcase.upcase_first)
@project.update_column(:project_language_id, db_language.id)
rescue

View File

@ -0,0 +1,56 @@
class MarkFilesController < ApplicationController
before_action :require_login
before_action :load_project
before_action :load_pull_request
def index
@files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token, { "only-file-name": true })
@mark_files = MarkFile.where(pull_request_id: @pull_request.id)
end
def create
# unless @pull_request.mark_files.present?
# MarkFile.bulk_insert(*%i[pull_request_id, file_path_sha file_path created_at updated_at]) do |worker|
# @files_result['Files'].each do |file|
# worker.add(pull_request_id: @pull_request.id, file_path_sha: SecureRandom.uuid.gsub("-", ""), file_path: file['Name'])
# end
# end
# end
end
def mark_file_as_unread
tip_exception "参数错误" if params[:file_path_sha].blank?
file_path = Base64.strict_decode64(params[:file_path_sha].to_s)
mark_file = @pull_request.mark_files.find_or_initialize_by(file_path_sha: params[:file_path_sha])
mark_file.file_path = file_path
mark_file.user_id = current_user.id
mark_file.mark_as_read = false
mark_file.save
render_ok
rescue Exception => e
tip_exception "参数解析错误"
end
def mark_file_as_read
tip_exception "参数错误" if params[:file_path_sha].blank?
file_path = Base64.strict_decode64(params[:file_path_sha].to_s)
mark_file = @pull_request.mark_files.find_or_initialize_by(file_path_sha: params[:file_path_sha])
mark_file.file_path = file_path
mark_file.user_id = current_user.id
mark_file.mark_as_read = true
mark_file.save
render_ok
rescue Exception => e
tip_exception "参数解析错误"
end
private
def review_params
params.require(:review).permit(:content, :commit_id, :status)
end
def load_pull_request
@pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
end
end

View File

@ -26,6 +26,9 @@ class MembersController < ApplicationController
@total_count = scope.size
@members = paginate(scope)
if @project.owner.is_a?(Organization) && (params[:page].to_i == 1 || params[:page].blank?) && !@project.members.exists?(user_id: current_user.id)
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
end
end
def remove
@ -61,11 +64,14 @@ class MembersController < ApplicationController
end
def check_member_exists!
return render_error("user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists?
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
return render_error("#{@user&.nickname}已经是项目成员") if member_exists? || (params[:user_id].to_i == current_user.id && @current_user_header_team.present?)
end
def check_member_not_exists!
return render_error("user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists?
@current_user_header_team = Team.joins(:team_users, :team_projects).where(team_projects: {project_id: @project.id}, team_users: {user_id: current_user.id}).order(authorize: :desc).take
return render_error("用户为组织成员,请到组织下操作!") if (params[:user_id].to_i == current_user.id && @current_user_header_team.present?) && !member_exists?
return render_error("#{@user&.nickname}还不是项目成员") unless member_exists?
end
def check_user_profile_completed

View File

@ -0,0 +1,17 @@
class NpsController < ApplicationController
before_action :require_login
# close,关闭
# createIssue,创建issue
# createPullRequest,创建PR
# auditPullRequest,审核PR
# indexProject,项目主页
# createProject,创建项目
# createOrganization,创建组织
def create
tip_exception "缺少参数" if params[:action_id].blank? || params[:action_type].blank?
UserNp.create(:action_id => params[:action_id].to_i, :action_type => params[:action_type], :user_id => User.current.id, :score => params[:score].to_f, memo: params[:memo])
render_ok
end
end

View File

@ -0,0 +1,130 @@
class ObRepositorySyncsController < ApplicationController
before_action :require_login
before_action :load_project
before_action :load_ob_repository_sync, except: [:create]
before_action :authenticate_user!
def index
render_ok(data: @ob_repository_sync)
end
def create
tip_exception "参数错误" if params[:github_address].blank? && params[:gitee_address].blank?
project_name ="#{@project.owner.name}:#{@project.identifier}"
service = ObRepositorySync::ApiService.new(project_name)
domain = GiteaService.gitea_config[:domain]
project_params = params.merge({ "gitlink_address": "#{domain}/#{@project.owner&.login}/#{@project.identifier}.git" })
res = service.create_projects(project_params)
tip_exception "保存失败: #{res["msg"]}" if res["code"].to_s != "200"
sync_id = res["data"]["id"]
ob_repository_sync = ObRepositorySync.find_or_initialize_by(project_id: @project.id)
ob_repository_sync.project_id = @project.id
ob_repository_sync.user_id = current_user.id
ob_repository_sync.name = project_name
ob_repository_sync.github_address = "#{params[:github_address]}"
ob_repository_sync.gitee_address = "#{params[:gitee_address]}"
ob_repository_sync.github_token = "#{params[:github_token]}"
ob_repository_sync.gitee_token = "#{params[:gitee_token]}"
ob_repository_sync.sync_id = sync_id
ob_repository_sync.save!
render_ok
end
def delete
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.delete_project @ob_repository_sync.sync_id
tip_exception "删除失败: #{res["msg"]}" if res["code"].to_s != "200"
if res["code"].to_s == "200"
@ob_repository_sync.destroy!
end
render_ok
end
def jobs
tip_exception "该项目未创建同步任务" if @ob_repository_sync.blank?
page = params[:page] || 1
limit = params[:limit] || 10
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
source = ""
if params[:type] && params[:type].to_s.downcase == "github"
source = "github_branch"
elsif params[:type] && params[:type].to_s.downcase == "gitee"
source = "gitee_branch"
end
res = service.get_projects_jobs(source, page, limit)
data = res["data"]["list"]
render_ok(count: res["data"]["total"], data: data)
end
def create_jobs
tip_exception "必须配置一个分支" if params[:github_branch].blank? && params[:gitee_branch].blank? && params[:gitlink_branch].blank?
ob_jobs = ObRepositorySyncJob.where(ob_repository_sync_id: @ob_repository_sync.id)
ob_jobs = ob_jobs.where(job_type: params[:job_type]) if params[:job_type].present?
ob_jobs = ob_jobs.where(github_branch: params[:github_branch]) if params[:github_branch].present?
ob_jobs = ob_jobs.where(gitee_branch: params[:gitee_branch]) if params[:gitee_branch].present?
ob_jobs = ob_jobs.where(gitlink_branch: params[:gitlink_branch]) if params[:gitlink_branch].present?
tip_exception "该分支组合已配置,不能重复!" if ob_jobs.count > 0
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.create_projects_jobs(params)
tip_exception "保存失败: #{res["msg"]}" if res["code"].to_s != "200"
job_id = res["data"]["id"]
job = ObRepositorySyncJob.new
job.ob_repository_sync_id = @ob_repository_sync.id
job.github_branch = "#{params[:github_branch]}"
job.gitee_branch = "#{params[:gitee_branch]}"
job.gitlink_branch = "#{params[:gitlink_branch]}"
job.job_type = "#{params[:job_type]}"
job.base = "#{params[:base]}"
job.job_id = job_id
job.save
render_ok
end
def delete_job
tip_exception "缺少参数job_id" if params[:job_id].blank?
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.delete_job params[:job_id]
tip_exception "删除失败: #{res["msg"]}" if res["code"].to_s != "200"
job = ObRepositorySyncJob.find_by(ob_repository_sync_id: @ob_repository_sync.id, job_id: params[:job_id])
job.destroy! if job.present?
render_ok
end
def start_job
tip_exception "缺少参数job_id" if params[:job_id].blank?
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.start_job params[:job_id]
tip_exception "启动错误: #{res["msg"]}" if res["code"].to_s != "200"
render_ok
end
def stop_job
tip_exception "缺少参数job_id" if params[:job_id].blank?
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.stop_job params[:job_id]
tip_exception "停止错误: #{res["msg"]}" if res["code"].to_s != "200"
render_ok
end
def job_logs
tip_exception "该项目未创建同步任务" if @ob_repository_sync.blank?
tip_exception "缺少参数job_id" if params[:job_id].blank?
service = ObRepositorySync::ApiService.new(@ob_repository_sync.name)
res = service.job_logs params[:job_id]
tip_exception "请求错误: #{res["msg"]}" if res["code"].to_s != "200"
render_ok(count: res["data"]["total"], data: res["data"]["list"])
end
private
def load_ob_repository_sync
@ob_repository_sync = ObRepositorySync.find_by(project_id: @project.id)
end
def authenticate_user!
return if @project.member?(current_user) || current_user.admin?
render_forbidden('你没有权限操作')
end
end

View File

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

View File

@ -31,6 +31,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate!
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
Cache::V2::OwnerCommonService.new(@organization.id).reset
end
rescue Exception => e
uid_logger_error(e.message)
@ -48,6 +49,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
Cache::V2::OwnerCommonService.new(@organization.id).reset
end
rescue Exception => e
uid_logger_error(e.message)

View File

@ -21,6 +21,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
tip_exception(e.message)
end
def create_all
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do
@organization.projects.each do |project|
TeamProject.build(@organization.id, @team.id, project.id)
end
Gitea::Organization::TeamProject::CreateAllService.call(@organization.gitea_token, @team.gtid, @organization.login)
render_ok
end
end
def destroy
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do
@ -33,6 +44,17 @@ class Organizations::TeamProjectsController < Organizations::BaseController
tip_exception(e.message)
end
def destroy_all
tip_exception("该组织团队项目包括组织所有项目,不允许更改") if @team.includes_all_project
ActiveRecord::Base.transaction do
@team.team_projects.each do |project|
project.destroy!
end
Gitea::Organization::TeamProject::DeleteAllService.call(@organization.gitea_token, @team.gtid, @organization.login)
render_ok
end
end
private
def load_organization
@organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id])
@ -47,7 +69,7 @@ class Organizations::TeamProjectsController < Organizations::BaseController
end
def load_operate_project
@operate_project = Project.find_by(id: project_mark) || Project.find_by(identifier: project_mark)
@operate_project = @organization.projects.where(id: project_mark).take || @organization.projects.where(identifier: project_mark).take
tip_exception("项目不存在") if @operate_project.nil?
end

View File

@ -9,7 +9,7 @@ class Projects::WebhooksController < Projects::BaseController
def create
ActiveRecord::Base.transaction do
return render_error("webhooks数量已到上限请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 19
return render_error("webhooks数量已到上限请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 49
return render_error("参数错误.") unless webhook_params.present?
form = Projects::Webhooks::CreateForm.new(webhook_params)
return render json: {status: -1, message: form.errors} unless form.validate!

View File

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

View File

@ -67,6 +67,8 @@ class PullRequestsController < ApplicationController
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
if @gitea_pull_request[:status] == :success
@pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
reviewers = User.where(id: params[:reviewer_ids])
@pull_request.reviewers = reviewers
SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
@ -112,6 +114,9 @@ class PullRequestsController < ApplicationController
end
end
reviewers = User.where(id: params[:reviewer_ids])
@pull_request.reviewers = reviewers
if @issue.update_attributes(@issue_params)
if @pull_request.update_attributes(@local_params.compact)
gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier,
@ -181,7 +186,7 @@ class PullRequestsController < ApplicationController
@issue_assign_to = @issue.get_assign_user
@gitea_pull = Gitea::PullRequest::GetService.call(@owner.login,
@repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
@last_review = @pull_request.issue.reviews.take
@last_review = @pull_request.reviews.take
end
def pr_merge
@ -268,7 +273,7 @@ class PullRequestsController < ApplicationController
def get_relatived
@project_tags = @project.issue_tags&.select(:id,:name, :color).as_json
@project_versions = @project.versions&.select(:id,:name, :status).as_json
@project_versions = @project.versions.opening&.select(:id,:name, :status).as_json
@project_members = @project.all_developers
@project_priories = IssuePriority&.select(:id,:name, :position).as_json
end

View File

@ -1,391 +1,407 @@
class RepositoriesController < ApplicationController
include RepositoriesHelper
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 :require_profile_completed, only: [:create_file]
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 files 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)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
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 = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
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
begin
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
if @sub_entries.blank? || @sub_entries['status'].to_i === -1
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
}
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
end
rescue
return render_error('该文件暂未开放,敬请期待.')
end
end
else
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
else
render_error(interactor.error)
end
end
end
def commits
if @project.educoder?
@commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
else
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end
end
end
def commits_slice
@hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
end
def commit
@sha = params[:sha]
if @project.educoder?
return render_error('暂未开放,敬请期待.')
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
end
end
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
if params[:filepath].present? || @project.educoder?
@contributors = []
else
result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
@contributors = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
rescue
@contributors = []
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
if params[:filepath].present?
result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
@path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
@readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
rescue
render json: nil
end
def languages
if @project.educoder?
render json: {}
else
render json: languages_precentagable
end
end
def archive
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
file_path = [domain, api_url, archive_url].join
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
def raw
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}"
file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
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
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
else
Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
end
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 RepositoriesHelper
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 :require_profile_completed, only: [:create_file]
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 files 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
@week_project_visit_record, @month_project_visit_record = TimeableVisitRecord.build(@project.id)
if @week_project_visit_record.visits < 300 && @month_project_visit_record.visits < 1000
@week_project_visit_record.increment!(:visits)
@month_project_visit_record.increment!(:visits)
@project.increment!(:visits)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
end
if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
@entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
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
begin
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
if @sub_entries.blank? || @sub_entries['status'].to_i === -1
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
}
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
end
rescue
return render_error('该文件暂未开放,敬请期待.')
end
end
else
@path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
else
render_error(interactor.error)
end
end
end
def commits
if @project.educoder?
@commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
else
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end
end
end
def commits_slice
@hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
end
def commit
@sha = params[:sha]
if @project.educoder?
return render_error('暂未开放,敬请期待.')
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
render_error(@commit[:message], @commit[:status]) if @commit.has_key?(:status) || @commit_diff.has_key?(:status)
end
end
def tags
if params[:only_name].present?
result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
else
name_result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])
@tag_names = result.is_a?(Hash) && result.key?(:status) ? [] : name_result
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
end
def contributors
if params[:filepath].present? || @project.educoder?
@contributors = []
else
result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
@contributors = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
rescue
@contributors = []
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
if params[:filepath].present?
result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
@path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
@readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
rescue
render json: nil
end
def languages
if @project.educoder?
render json: {}
else
render json: languages_precentagable
end
end
def archive
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"
file_path = [domain, api_url, archive_url].join
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
def raw
domain = GiteaService.gitea_config[:domain]
api_url = GiteaService.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{Addressable::URI.escape(params[:filepath])}?ref=#{Addressable::URI.escape(params[:ref])}"
file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
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
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
else
Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
end
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

View File

@ -5,7 +5,7 @@ class ReviewsController < ApplicationController
def create
return render_forbidden('您不是审查人员,无法进行审查!') if current_user&.id != @pull_request.issue.assigned_to_id
@journal, @review = Api::V1::Projects::PullRequests::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
@review = Api::V1::Projects::Pulls::Reviews::CreateService.call(@project, @pull_request, review_params, current_user)
end
private

View File

@ -1,7 +1,7 @@
class Traces::BaseController < ApplicationController
helper_method :observed_logged_user?, :observed_user
before_action :check_trace_system
def observed_user
@_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id]))
@ -15,4 +15,12 @@ class Traces::BaseController < ApplicationController
def check_auth
return render_forbidden unless current_user.admin? || observed_logged_user?
end
def check_trace_system
code, data, error = Trace::SystemInfoService.call(current_user.trace_token)
return render_ok({code: 501, data: {operate_time: data['operate_time']}, message: '系统维护中'}) if data['status'] === 0
rescue
# 这里根据需求跳转到404
return render_not_found
end
end

View File

@ -3,6 +3,8 @@ class Traces::ProjectsController < Traces::BaseController
before_action :require_login
before_action :load_project
before_action :require_project_not_be_forked_project
before_action :set_trace_token_to_cookie
before_action :authorizate_user_can_edit_project!, except: [:task_results]
def tasks
@ -35,7 +37,7 @@ class Traces::ProjectsController < Traces::BaseController
return render :json => {left_tasks_count: 5, data: []} if current_user.trace_user.nil?
code, data, error = Trace::CheckResultService.call(current_user.trace_token, @project, nil, page, limit)
if code == 200
render :json => {left_tasks_count: 5 - @project.user_trace_tasks.size, data: data}
render :json => {left_tasks_count: 5 - @project.user_trace_tasks.size, data: data, view_base: "#{Trace.trace_config[:view_domain]}/analysis_ccf/analysis-results/" }
else
render_error("获取检测记录失败 Error:#{error}")
end
@ -86,4 +88,16 @@ class Traces::ProjectsController < Traces::BaseController
puts exception.message
normal_status(-1, exception.message)
end
def set_trace_token_to_cookie
cookies[:vue_admin_template_token] = {
:value => current_user&.trace_token,
:expires => 1.hours.from_now,
:domain => Trace.trace_config[:cookie_domain]
}
end
def require_project_not_be_forked_project
return render_error('fork仓库暂不支持代码溯源服务敬请谅解。') if @project.forked_from_project_id.present?
end
end

View File

@ -1,7 +1,10 @@
class Users::IsPinnedProjectsController < Users::BaseController
before_action :private_user_resources!, only: [:pin]
def index
@is_pinned_projects = observed_user.pinned_projects.order(position: :desc, created_at: :asc).includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
def index
@is_pinned_projects = observed_user.pinned_projects.left_joins(:project)
.where("projects.is_public = TRUE")
.order(position: :desc, created_at: :asc)
.includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
@is_pinned_projects = kaminari_paginate(@is_pinned_projects)
end

View File

@ -109,6 +109,19 @@ class UsersController < ApplicationController
@user = current_user
end
# cloudIDE saas定制
def info
@code = 1001
@message = "用户不存在"
if params[:token].present?
@user = User.try_to_autologin(params[:token])
if @user.present?
@code = 1000
@message = "success"
end
end
end
# 贴吧获取用户信接口
def get_user_info
begin
@ -349,7 +362,7 @@ class UsersController < ApplicationController
# 通过login参数查询头歌账号信息注册并登录
def autologin_register_by_educoder(edu_login)
req_params = { "login" => "#{edu_login}", "private_token" => "hriEn3UwXfJs3PmyXnSH" }
req_params = { "login" => "#{edu_login}", "private_token" => "hriEn3UwXfJs3PmyXnqQ" }
api_url= "https://data.educoder.net"
client = Faraday.new(url: api_url)
response = client.public_send("get", "/api/sources/get_user_info_by_login", req_params)

File diff suppressed because it is too large Load Diff

View File

@ -1126,21 +1126,21 @@ await octokit.request('POST /api/v1/yystopf/ceshi/contents/batch.json')
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|files.action_type |是| |string|操作类型 create: 创建 update: 更新 delete: 删除|
|files.content |是| |string|文件内容|
|files.encoding |是| |string|文件编码方式 text 文本 base64 加密|
|files.file_path |是| |string|文件路径|
|author_email |是| |string|作者邮箱|
|author_name |是| |string|作者名称|
|author_timeunix |是| |int|编码时间,精确到秒|
|committer_email |是| |string|提交者邮箱|
|committer_name |是| |string|提交者名称|
|committer_timeunix|是| |int|提交时间戳,精确到秒|
|branch |是| |string|提交分支|
|new_branch |否| |string|如果需要创建新分支,这个需要填|
|message |是| |string|提交信息|
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|files.action_type |是| |string|操作类型 create: 创建 update: 更新 delete: 删除|
|files.content |是| |string|文件内容|
|files.encoding |是| |string|文件编码方式 text 文本 base64 加密|
|files.file_path |是| |string|文件路径|
|author_email |否| 当前用户邮箱 |string|作者邮箱,不填时需要与作者名称同时为空|
|author_name |否| 当前用户标识 |string|作者名称,不填时需要与作者邮箱同时为空|
|author_timeunix |否| 当前时间戳 |int|编码时间,精确到秒|
|committer_email |否| 当前用户邮箱 |string|提交者邮箱,不填时需要与提交者名称同时为空|
|committer_name |否| 当前用户标识 |string|提交者名称,不填时需要与提交者邮箱同时为空|
|committer_timeunix|否| 当前时间戳 |int|提交时间戳,精确到秒|
|branch |是| |string|提交分支|
|new_branch |否| |string|如果需要创建新分支,这个需要填|
|message |是| |string|提交信息|
> 请求的JSON示例:
@ -1990,25 +1990,27 @@ await octokit.request('GET /api/v1/yystopf/csfjkkj/compare.json')
|diff.files.is_created|bool|是否为新建文件|
|diff.files.is_deleted|bool|是否为删除文件|
|diff.files.is_bin|bool|是否为二进制文件|
|diff.files.is_lfs_file|bool||
|diff.files.is_lfs_file|bool|是否为LFS文件|
|diff.files.is_renamed|bool|是否重命名|
|diff.files.is_ambiguous|bool||
|diff.files.is_submodule|bool|是否为子模块|
|diff.files.sections.file_name|string|文件名称|
|diff.files.sections.name|string||
|diff.files.sections.lines.left_index|int||
|diff.files.sections.lines.right_index|int||
|diff.files.sections.lines.left_index|int|文件变动之前所在行数|
|diff.files.sections.lines.right_index|int|文件变动之后所在行数|
|diff.files.sections.lines.match|int||
|diff.files.sections.lines.type|int||
|diff.files.sections.lines.content|string||
|diff.files.sections.lines.section_path|string||
|diff.files.sections.lines.type|int|文件变更类型|
|diff.files.sections.lines.content|string|文件变更内容|
|diff.files.sections.lines.section_path|string|文件路径|
|diff.files.sections.lines.section_last_left_index|int||
|diff.files.sections.lines.section_last_right_index|int||
|diff.files.sections.lines.section_left_index|int||
|diff.files.sections.lines.section_right_index|int||
|diff.files.sections.lines.section_left_hunk_size|int||
|diff.files.sections.lines.section_right_hunk_size|int||
|diff.files.sections.lines.section_left_index|int|文件变更之前所在行数|
|diff.files.sections.lines.section_right_index|int|文件变更之后所在行数(即:页面编辑器开始显示的行数)|
|diff.files.sections.lines.section_left_hunk_size|int|文件变更之前的行数|
|diff.files.sections.lines.section_right_hunk_size|int|文件变更之后的行数(及当前页面编辑器显示的总行数)|
|diff.files.is_incomplete|bool|是否不完整|
|diff.files.is_incomplete_line_too_long|bool|文件是否不完整是因为太长了|
|diff.files.is_protected|bool|文件是否被保护|
> 返回的JSON示例:
@ -2281,7 +2283,7 @@ await octokit.request('POST /api/v1/yystopf/ceshi/webhooks.json')
|webhook.active |是| | bool | 是否激活|
|webhook.branch_filter|否| |string|分支过滤|
|webhook.events |否| |array|触发事件|
|webhook.type |否| gitea |string| hook类型gitea slack discord dingtalk telegram msteams feishu matrix jianmu|
触发事件字段说明
参数| 含义|

View File

@ -0,0 +1,78 @@
# Teams
## 团队下新增所有的项目
团队下新增所有的项目
> 示例:
```shell
curl -X POST \
http://localhost:3000/api/organizations/ceshi_org/teams/28/team_projects/create_all
```
```javascript
await octokit.request('POST /api/organizations/ceshi_org/teams/28/team_projects/create_all.json')
```
### HTTP 请求
`POST /api/organizations/:organization/teams/:id/team_projects/create_all.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|organization |是| | string |组织标识 |
|id |是| | integer|团队 ID|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success Data.
</aside>
## 团队下删除所有的项目
团队下删除所有的项目
> 示例:
```shell
curl -X DELETE \
http://localhost:3000/api/organizations/ceshi_org/teams/28/team_projects/destroy_all
```
```javascript
await octokit.request('DELETE /api/organizations/ceshi_org/teams/28/team_projects/destroy_all.json')
```
### HTTP 请求
`DELETE /api/organizations/:organization/teams/:id/team_projects/destroy_all.json`
### 请求参数:
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|organization |是| | string |组织标识 |
|id |是| | integer|团队 ID|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
<aside class="success">
Success Data.
</aside>

View File

@ -2304,4 +2304,189 @@ await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
"created_at": "2021-06-09 16:41",
"time_ago": "7分钟前"
}
```
## 用户发送邮件验证码
用户发送邮件验证码
> 示例:
```shell
curl -X GET http://localhost:3000/api/v1/yystopf/send_email_vefify_code.json
```
```javascript
await octokit.request('GET /api/v1/:login/send_email_vefify_code.json')
```
### HTTP 请求
`GET /api/v1/:login/send_email_vefify_code.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|code_type |int |10: 更新邮箱|
|email |string |邮箱|
|smscode |string |邮箱md5加密值|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证邮件验证码
用户验证邮件验证码
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_email_verify_code.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_email_verify_code.json')
```
### HTTP 请求
`POST /api/v1/:login/check_email_verify_code.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|code_type |int |10: 更新邮箱|
|email |string |邮箱|
|code |string |邮箱验证码|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证密码
用户验证密码,检查是否和用户密码一致
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_password.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_password.json')
```
### HTTP 请求
`POST /api/v1/:login/check_password.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|password |string |用户密码|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户验证邮箱
用户验证邮箱是否符合规范以及是否已被使用
> 示例:
```shell
curl -X POST http://localhost:3000/api/v1/yystopf/check_email.json
```
```javascript
await octokit.request('POST /api/v1/:login/check_email.json')
```
### HTTP 请求
`POST /api/v1/:login/check_email.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|email |string |邮箱地址|
### 返回字段说明:
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 用户更改邮箱
用户更改一个新的邮箱
> 示例:
```shell
curl -X PATCH http://localhost:3000/api/v1/yystopf/update_email.json
```
```javascript
await octokit.request('PATCH /api/v1/:login/update_email.json')
```
### HTTP 请求
`PATCH /api/v1/:login/update_email.json`
### 请求字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|login |string |用户标识 |
|password |string |用户密码|
|email |string |邮箱地址|
|code |string |邮箱验证码|
> 请求的JSON示例:
```json
{
"password": "Aa19960425.",
"code": "657134",
"email": "yystopf@163.com"
}
```
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```

View File

@ -3,6 +3,8 @@ class Contents::CreateForm < BaseForm
validates :filepath, presence: true
validates :new_branch, length: { maximum: 100, too_long: "过长,仅支持%{count}的长度"}
validate :check_branch
def check_branch

View File

@ -0,0 +1,2 @@
module Admins::NpsHelper
end

View File

@ -95,21 +95,31 @@ module ApplicationHelper
timePassed = currentTime - lastUpdateTime
timeIntoFormat = 0
updateAtValue = ""
if timePassed < 0
if timePassed <= 0
updateAtValue = "刚刚"
elsif timePassed < 2 * 1000
updateAtValue = "1秒前"
elsif timePassed < ONE_MINUTE
updateAtValue = "1分钟前"
elsif timePassed < ONE_HOUR
timeIntoFormat = timePassed / ONE_MINUTE
updateAtValue = timeIntoFormat.to_s + "分钟前"
elsif (timePassed < ONE_DAY)
timeIntoFormat = (timePassed.to_f / ONE_HOUR).ceil
timeIntoFormat = (timePassed.to_f / ONE_HOUR).round
timeIntoFormat == 1 if timeIntoFormat.to_i == 0
updateAtValue = timeIntoFormat.to_s + "小时前"
elsif (timePassed < ONE_MONTH)
timeIntoFormat = (timePassed.to_f / ONE_DAY).ceil
timeIntoFormat = (timePassed.to_f / ONE_DAY).round
timeIntoFormat == 1 if timeIntoFormat.to_i == 0
updateAtValue = timeIntoFormat.to_s + "天前"
elsif (timePassed < ONE_MONTH)
timeIntoFormat = (timePassed.to_f / ONE_DAY).round
timeIntoFormat == 1 if timeIntoFormat.to_i == 0
updateAtValue = timeIntoFormat.to_s + "天前"
elsif (timePassed < ONE_YEAR)
timeIntoFormat = (timePassed.to_f / ONE_MONTH).ceil
timeIntoFormat = (timePassed.to_f / ONE_MONTH).round
timeIntoFormat == 1 if timeIntoFormat.to_i == 0
updateAtValue = timeIntoFormat.to_s + "个月前"
else
timeIntoFormat = timePassed / ONE_YEAR

View File

@ -63,6 +63,9 @@ module ProjectsHelper
project_category_id: project.project_category_id,
project_language_id: project.project_language_id,
license_id: project.license_id,
jianmu_devops: jianmu_devops_code(project, user),
jianmu_devops_url: jianmu_devops_url,
cloud_ide_saas_url: cloud_ide_saas_url(user),
ignore_id: project.ignore_id
}).compact
@ -98,4 +101,53 @@ module ProjectsHelper
def render_educoder_avatar_url(project_educoder)
[Rails.application.config_for(:configuration)['educoder']['cdn_url'], project_educoder&.image_url].join('/')
end
# 静默登录方式:
#
# 数据格式为JSON
# {
# "userId": "xxx", // 用户唯一标识
# "ref": "xxx", // 仓库唯一标识
# "owner": "xxx", // 用户登录名或组织账号
# "timestamp": xxx // 当前时间戳,单位:毫秒
# }
# 加密方式把数据序列化成JSON字符串用Client Secret和固定IV5183666c72eec9e4对称加密模式AES-256-CBC
#
# API
# GEThttps://ci-v3.test.jianmuhub.com/oauth2/authorize?code=${encode(密文)}
def jianmu_devops_code(project, user)
if user.admin? || project.member?(user.id)
data = { userId: user.id, ref: project.identifier, owner: project.owner.login, timestamp: Time.now.to_i * 1000 }
# uid = EduSetting.get("jianmu_oauth2_uid") || 'oedKx4v-WyAfu2oy_AsFpFQCH_-g91ms0PQKN7YcEuw'
# app = Doorkeeper::Application.find_by(uid: uid)
key = 'bf3c199c2470cb477d907b1e0917c17b'
aes_encrypt(key, data.to_json)
end
end
def jianmu_devops_url
EduSetting.get("jianmu_devops_url") || "https://ci-v3.test.jianmuhub.com"
end
def cloud_ide_saas_url(user)
"" unless user.logged?
token = Token.get_token_from_user(user, "autologin")
oauth_url = "#{Rails.application.config_for(:configuration)['platform_url']}/api/users/info.json"
saas_url = EduSetting.get("cloud_ide_saas_url") || "https://saasfactory.test.opentrs.com"
"#{saas_url}/oauth/login?product_account_id=PA1001218&tenant_code=TI1001383&oauth_url=#{oauth_url}&token=#{token.value}"
end
def aes_encrypt(key, des_text)
# des_text='{"access_key_id":"STS.NTuC9RVmWfJqj3JkcMzPnDf7X","access_key_secret":"E8NxRZWGNxxMfwgt5nFLnBFgg6AzgXCZkSNCyqygLuHM","end_point":"oss-accelerate.aliyuncs.com","security_token":"CAIS8gF1q6Ft5B2yfSjIr5fACIPmu7J20YiaaBX7j2MYdt9Cq6Ocujz2IHhMenVhA+8Wv/02n2hR7PcYlq9IS55VWEqc/VXLaywQo22beIPkl5Gfz95t0e+IewW6Dxr8w7WhAYHQR8/cffGAck3NkjQJr5LxaTSlWS7OU/TL8+kFCO4aRQ6ldzFLKc5LLw950q8gOGDWKOymP2yB4AOSLjIx6lAt2T8vs/7hmZPFukSFtjCglL9J/baWC4O/csxhMK14V9qIx+FsfsLDqnUIs0YWpf0p3P0doGyf54vMWUM05A6dduPS7txkLAJwerjVl1/ADxc0/hqAASXhPeiktbmDjwvnSn4iKcSGQ+xoQB468eHXNdvf13dUlbbE1+JhRi0pZIB2UCtN9oTsLHcwIHt+EJaoMd3+hGwPVmvHSXzECDFHylZ8l/pzTwlE/aCtZyVmI5cZEvmWu2xBa3GRbULo7lLvyeX1cHTVmVWf4Nk6D09PzTU8qlAj","bucket":"edu-bigfiles1","region":"oss-cn-hangzhou","callback_url":"https://data.educoder.net/api/buckets/callback.json","bucket_host":"data.educoder.net"}'
# des = OpenSSL::Cipher::Cipher.new('aes-256-ctr')
des = OpenSSL::Cipher.new('AES-256-CBC')
des.encrypt
# des.padding =
des.key = key
des.iv = "5183666c72eec9e4"
result = des.update(des_text)
result << des.final
Base64.strict_encode64 result
end
end

View File

@ -123,7 +123,7 @@ module TagChosenHelper
cache_key = "project-#{project.id}/all_milestones/size-#{project.versions.size}/#{project.versions.maximum('updated_on')}"
Rails.cache.fetch(cache_key) do
project.versions.select(:id, :name, :status).collect do |event|
project.versions.opening.select(:id, :name, :status).collect do |event|
{
id: event.id,
name: event.name,

View File

@ -1,6 +1,6 @@
module CustomRegexp
PHONE = /1\d{10}/
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
EMAIL = /\A[a-zA-Z0-9]+([._\-\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]{4,15}\z/ #只含有数字、字母、下划线不能以下划线开头和结尾
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/

View File

@ -1,11 +1,16 @@
class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致
default from: 'notification@trustie.org'
# 用户注册验证码
def register_email(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 注册验证码')
end
end
class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致
# default from: 'notification@trustie.org'
default from: 'noreply@gitlink.org.cn'
# 用户注册验证码
def register_email(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 注册验证码')
end
def update_email(mail, code)
@code = code
mail(to: mail, subject: 'Gitink | 更改邮箱验证码')
end
end

View File

@ -17,30 +17,69 @@ module ProjectOperable
owner.build_permit_team_projects!(id)
# 避免自己创建的项目,却无法拥有访问权,因为该用户所在团队暂未获得项目访问权
return if creator.nil? || owner.is_owner?(creator.id)
add_member!(creator.id, "Manager")
add_member!(creator.id, "Manager") if creator.is_a?(User)
end
def add_member!(user_id, role_name='Developer')
if self.owner.is_a?(Organization)
case role_name
when 'Manager'
# 构建相应的团队
team = self.owner.teams.admin.take
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
if team.nil?
team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 新增对应的团队成员
team_user = TeamUser.build(self.user_id, user_id, team.id)
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
# 确保组织成员中有该用户
OrganizationUser.build(self.user_id, user_id)
when 'Developer'
# 构建相应的团队
team = self.owner.teams.write.take
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
if team.nil?
team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 新增对应的团队成员
team_user = TeamUser.build(self.user_id, user_id, team.id)
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
# 确保组织成员中有该用户
OrganizationUser.build(self.user_id, user_id)
when 'Reporter'
# 构建相应的团队
team = self.owner.teams.read.take
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
if team.nil?
team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 新增对应的团队成员
team_user = TeamUser.build(self.user_id, user_id, team.id)
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
# 确保组织成员中有该用户
OrganizationUser.build(self.user_id, user_id)
end
end
member = members.create!(user_id: user_id, team_user_id: team_user&.id)
@ -71,26 +110,70 @@ module ProjectOperable
def change_member_role!(user_id, role)
member = self.member(user_id)
# 所有者为组织,并且该用户属于组织成员
if self.owner.is_a?(Organization) && member.team_user.present?
case role&.name
when 'Manager'
# 构建相应的团队
team = self.owner.teams.admin.take
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
if team.nil?
team = Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 更改对应的团队成员
team_user = member.team_user
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
# 确保组织成员中有该用户
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
when 'Developer'
# 构建相应的团队
team = self.owner.teams.write.take
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
if team.nil?
team = Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
$gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 更改对应的团队成员
team_user = member.team_user
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
when 'Reporter'
# 构建相应的团队
team = self.owner.teams.read.take
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
if team.nil?
team = Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false)
gteam = $gitea_client.post_orgs_teams_by_org(self.owner.login, {body: team.to_gitea_hash.to_json}) rescue nil
team.update_attributes!({gtid: gteam["id"]}) unless gteam.nil?
end
# 设置项目在团队中的访问权限
team_project = TeamProject.build(self.user_id, team.id, self.id)
tp_result = $gitea_client.put_teams_repos_by_id_org_repo(team.gtid, self.owner.login, self.identifier) rescue nil
# 更改对应的团队成员
team_user = member.team_user
$gitea_client.delete_teams_members_by_id_username(team_user.team.gtid, team_user.user&.login) rescue nil # 移除旧的
$gitea_client.put_teams_members_by_id_username(team&.gtid, team_user.user&.login) rescue nil # 新增新的
team_user.update_attributes!({team_id: team.id}) unless team.team_users.exists?(user_id: member.user_id)
# 确保组织成员中有该用户
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
end
end
member.member_roles.last.update_attributes!(role: role)

View File

@ -11,4 +11,7 @@
class Ignore < ApplicationRecord
include Projectable
validates :name, :content, presence: true
validates :name, uniqueness: { case_sensitive: false }
end

View File

@ -69,7 +69,6 @@ class Issue < ApplicationRecord
has_many :issue_tags, through: :issue_tags_relates
has_many :issue_times, dependent: :destroy
has_many :issue_depends, dependent: :destroy
has_many :reviews, dependent: :destroy
scope :issue_includes, ->{includes(:user)}
scope :issue_many_includes, ->{includes(journals: :user)}
scope :issue_issue, ->{where(issue_classify: [nil,"issue"])}

View File

@ -13,6 +13,15 @@
# comments_count :integer default("0")
# reply_id :integer
# review_id :integer
# commit_id :string(255)
# diff :text(4294967295)
# line_code :string(255)
# path :string(255)
# state :integer default("0")
# resolve_at :datetime
# resolveer_id :integer
# need_respond :boolean default("0")
# updated_on :datetime
#
# Indexes
#
@ -24,15 +33,22 @@
#
class Journal < ApplicationRecord
serialize :diff, JSON
alias_attribute :note, :notes
belongs_to :user
belongs_to :issue, foreign_key: :journalized_id, :touch => true
belongs_to :issue, foreign_key: :journalized_id, :touch => true, optional: true
belongs_to :journalized, polymorphic: true
belongs_to :review, optional: true
belongs_to :resolveer, class_name: 'User', foreign_key: :resolveer_id, optional: true
has_many :journal_details, :dependent => :delete_all
has_many :attachments, as: :container, dependent: :destroy
has_many :children_journals, class_name: 'Journal', foreign_key: :parent_id
scope :journal_includes, ->{includes(:user, :journal_details, :attachments)}
scope :parent_journals, ->{where(parent_id: nil)}
scope :children_journals, lambda{|journal_id| where(parent_id: journal_id)}
enum state: {opened: 0, resolved: 1, disabled: 2}
def is_journal_detail?
self.notes.blank? && self.journal_details.present?

View File

@ -11,4 +11,8 @@
class License < ApplicationRecord
include Projectable
validates :name, :content, presence: true
validates :name, uniqueness: { case_sensitive: false }
end

5
app/models/mark_file.rb Normal file
View File

@ -0,0 +1,5 @@
class MarkFile < ApplicationRecord
belongs_to :pull_request
end

View File

@ -13,6 +13,7 @@
#
class MessageTemplate < ApplicationRecord
# self.inheritance_column = nil
PLATFORM = 'GitLink'
def self.build_init_data
@ -113,6 +114,6 @@ class MessageTemplate < ApplicationRecord
end
def simple_type
self.type.split("::")[-1]
self.type.to_s.split("::")[-1]
end
end

View File

@ -0,0 +1,28 @@
# == Schema Information
#
# Table name: ob_repository_syncs
#
# id :integer not null, primary key
# project_id :integer
# user_id :integer
# name :string(255)
# github_address :string(255)
# gitee_address :string(255)
# github_token :string(255)
# gitee_token :string(255)
# sync_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_ob_repository_syncs_on_project_id (project_id)
# index_ob_repository_syncs_on_user_id (user_id)
#
class ObRepositorySync < ApplicationRecord
belongs_to :project
belongs_to :user
has_many :ob_repository_sync_jobs, dependent: :destroy
end

View File

@ -0,0 +1,24 @@
# == Schema Information
#
# Table name: ob_repository_sync_jobs
#
# id :integer not null, primary key
# ob_repository_sync_id :integer
# github_branch :string(255)
# gitee_branch :string(255)
# gitlink_branch :string(255)
# job_type :string(255)
# base :string(255)
# job_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_ob_repository_sync_jobs_on_ob_repository_sync_id (ob_repository_sync_id)
#
class ObRepositorySyncJob < ApplicationRecord
belongs_to :ob_repository_sync
end

View File

@ -81,15 +81,15 @@ class Organization < Owner
scope :with_visibility, ->(visibility) { joins(:organization_extension).where(organization_extensions: {visibility: visibility}) if visibility.present? }
after_save :reset_cache_data
# after_save :reset_cache_data
def gitea_token
team_users.joins(:team).where(teams: {authorize: "owner"}).take&.user&.gitea_token
end
def reset_cache_data
Cache::V2::OwnerCommonService.new(self.id).reset
end
# def reset_cache_data
# Cache::V2::OwnerCommonService.new(self.id).reset
# end
def self.build(name, nickname, gitea_token=nil)
self.create!(login: name, nickname: nickname, gitea_token: gitea_token)

View File

@ -12,4 +12,6 @@
class ProjectLanguage < ApplicationRecord
include Projectable
validates :name, uniqueness: { message: "已存在" }
end

View File

@ -32,12 +32,18 @@ class PullRequest < ApplicationRecord
belongs_to :issue
belongs_to :user
belongs_to :project, counter_cache: true, touch: true
# belongs_to :fork_project, foreign_key: :fork_project_id
belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id, optional: true
has_many :pull_request_assigns, foreign_key: :pull_request_id
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_id, class_name: 'Gitea::Pull'
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :journal_details, through: :journals
has_many :reviews, dependent: :destroy
has_many :pull_requests_reviewers, dependent: :destroy
has_many :reviewers, through: :pull_requests_reviewers
has_many :mark_files, dependent: :destroy
scope :merged_and_closed, ->{where.not(status: 0)}
scope :opening, -> {where(status: 0)}

View File

@ -0,0 +1,21 @@
# == Schema Information
#
# Table name: pull_requests_reviewers
#
# id :integer not null, primary key
# pull_request_id :integer
# reviewer_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_pull_requests_reviewers_on_pull_request_id (pull_request_id)
# index_pull_requests_reviewers_on_reviewer_id (reviewer_id)
#
class PullRequestsReviewer < ApplicationRecord
belongs_to :pull_request
belongs_to :reviewers, class_name: 'User', foreign_key: :reviewer_id
end

View File

@ -49,7 +49,7 @@ class Repository < ApplicationRecord
end
def url
self['url'].blank? ? "#{Rails.application.config_for(:configuration)['platform_url']}/#{self.owner&.login}/#{self.identifier}.git" : self['url']
self['url'].blank? ? "#{GiteaService.gitea_config[:domain]}/#{self.owner&.login}/#{self.identifier}.git" : self['url']
end
# with repository is mirror

View File

@ -2,24 +2,24 @@
#
# Table name: reviews
#
# id :integer not null, primary key
# issue_id :integer
# reviewer_id :integer
# content :text(65535)
# commit_id :string(255)
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# id :integer not null, primary key
# reviewer_id :integer
# content :text(65535)
# commit_id :string(255)
# status :integer default("0")
# created_at :datetime not null
# updated_at :datetime not null
# pull_request_id :integer
#
# Indexes
#
# index_reviews_on_issue_id (issue_id)
# index_reviews_on_reviewer_id (reviewer_id)
# index_reviews_on_pull_request_id (pull_request_id)
# index_reviews_on_reviewer_id (reviewer_id)
#
class Review < ApplicationRecord
belongs_to :issue
belongs_to :pull_request
belongs_to :reviewer, class_name: 'User', foreign_key: :reviewer_id
has_one :journal, dependent: :destroy

View File

@ -70,4 +70,15 @@ class Team < ApplicationRecord
end
end
def to_gitea_hash
{
can_create_org_repo: self.can_create_org_project,
description: self.description || "",
includes_all_repositories: self.includes_all_project,
name: self.name,
permission: self.authorize,
units: self.team_units.pluck(:unit_type).map{|i| "repo.#{i}"}
}
end
end

View File

@ -0,0 +1,28 @@
# == Schema Information
#
# Table name: timeable_visit_records
#
# id :integer not null, primary key
# time :string(255)
# project_id :integer
# visits :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_timeable_visit_records_on_project_id (project_id)
# index_timeable_visit_records_on_time (time)
#
class TimeableVisitRecord < ApplicationRecord
belongs_to :project
def self.build(project_id)
week = TimeableVisitRecord.find_or_create_by!(time: Date.today.strftime("%Y-%W"), project_id: project_id)
month = TimeableVisitRecord.find_or_create_by!(time: Date.today.strftime("%Y%m"), project_id: project_id)
return week, month
end
end

View File

@ -32,7 +32,7 @@ class TraceUser < ApplicationRecord
self.username = user.login
self.password = SecureRandom.hex
self.unit = user.custom_department.blank? ? 'GitLink' : user.custom_department
self.telnumber = user.phone.blank? ? '13800000000' : user.phone
self.telnumber = user.phone || ""
self.email = user.mail
self.name = user.nickname.blank? ? user.login : user.nickname
@ -41,7 +41,7 @@ class TraceUser < ApplicationRecord
def build_token
return if username.blank? || password.blank? || unit.blank? || telnumber.blank? || email.blank? || name.blank?
return if username.blank? || password.blank? || unit.blank? || email.blank? || name.blank?
response = Trace::AddUserService.call(username, password, unit, telnumber, email, name)
self.token = response[1]['token']
@ -49,7 +49,7 @@ class TraceUser < ApplicationRecord
end
def refresh_token
return if username.blank? || password.blank? || unit.blank? || telnumber.blank? || email.blank? || name.blank?
return if username.blank? || password.blank? || unit.blank? || email.blank? || name.blank?
response = Trace::LoginService.call(username, password)
self.token = response[1]['token']

21
app/models/user_np.rb Normal file
View File

@ -0,0 +1,21 @@
# == Schema Information
#
# Table name: user_nps
#
# id :integer not null, primary key
# user_id :integer
# action_type :string(255)
# action_id :integer
# score :float(24)
# memo :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_user_nps_on_user_id (user_id)
#
class UserNp < ApplicationRecord
belongs_to :user, optional: true
end

View File

@ -32,7 +32,8 @@ class VersionRelease < ApplicationRecord
has_many :attachments, as: :container, dependent: :destroy
def update_sha
return if version_gid.blank?
git_release = Gitea::Versions::GetService.call(user.gitea_token, repository&.owner&.login, repository&.identifier, version_gid)
self.update(sha: git_release["sha"])
self.update(sha: git_release["sha"]) if git_release.present? && git_release.is_a?(Hash)
end
end

View File

@ -19,6 +19,9 @@ class Admins::UserQuery < ApplicationQuery
status = params[:status]
users = users.where(status: status) if status.present?
# 是否管理员
users = users.where(admin: params[:admin]) if params[:admin].present?
# 职业
users = users.joins(:user_extension).where(user_extensions: { identity: params[:identity] }) if params[:identity].present?
@ -27,6 +30,15 @@ class Admins::UserQuery < ApplicationQuery
users = users.joins(user_extension: :school).where(schools: { auto_users_trial: params[:auto_trial].to_i == 1 })
end
# 第三方账户类型
if params[:open_user_type].present?
if params[:open_user_type] == "Forge"
users = users.left_joins(:open_users).where(open_users: {id: nil})
else
users = users.joins(:open_users).where(open_users: {type: params[:open_user_type]})
end
end
# 关键字检索
keyword = params[:keyword].to_s.strip.presence
if keyword

View File

@ -29,6 +29,8 @@ class Projects::ListMyQuery < ApplicationQuery
projects = projects.where(user_id: user.id)
elsif params[:category].to_s == "watched" #我关注的
projects = projects.where.not(user_id: user.id).joins(:watchers).where(watchers: {watchable_type: "Project", user_id: user.id})
elsif params[:category].to_s == 'only_watched'
projects = projects.joins(:watchers).where(watchers: {watchable_type: "Project", user_id: user.id})
elsif params[:category].to_s == "forked" #我fork的
fork_ids = user.fork_users.select(:id, :fork_project_id).pluck(:fork_project_id)
projects = projects.where(id: fork_ids)

View File

@ -11,7 +11,7 @@ class Projects::ListQuery < ApplicationQuery
end
def call
collection = Project.all
collection = Project.visible
collection = filter_projects(collection)
sort = params[:sort_by] || "updated_on"
@ -25,7 +25,7 @@ class Projects::ListQuery < ApplicationQuery
def filter_projects(collection)
# collection = by_pinned(collection)
collection = by_search(collection)
collection = by_search(collection) if params[:search].present?
collection = by_project_type(collection)
collection = by_project_category(collection)
collection = by_project_language(collection)
@ -33,7 +33,13 @@ class Projects::ListQuery < ApplicationQuery
end
def by_search(items)
items.visible.by_name_or_identifier(params[:search])
ids = Projects::ElasticsearchService.call(params[:search])
items = items.where(platform: 'forge')
if ids.present?
items.where(id: ids).by_name_or_identifier(params[:search])
else
items.by_name_or_identifier(params[:search])
end
end
def by_project_type(items)

View File

@ -13,7 +13,6 @@ class Api::V1::Projects::Contents::BatchCreateService < ApplicationService
def initialize(project, params, token=nil)
puts params
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@ -21,11 +20,11 @@ class Api::V1::Projects::Contents::BatchCreateService < ApplicationService
@files = params[:files]
@author_email = params[:author_email]
@author_name = params[:author_name]
@author_timeunix = params[:author_timeunix]
@author_timeunix = params[:author_timeunix] || Time.now.to_i
@branch = params[:branch]
@committer_email = params[:committer_email]
@committer_name = params[:committer_name]
@committer_timeunix = params[:committer_timeunix]
@committer_timeunix = params[:committer_timeunix] || Time.now.to_i
@message = params[:message]
@new_branch = params[:new_branch]
end
@ -63,6 +62,7 @@ class Api::V1::Projects::Contents::BatchCreateService < ApplicationService
committer: Time.at(committer_timeunix.to_i)
},
message: message,
branch: branch,
new_branch: new_branch,
signoff: false
}
@ -77,15 +77,16 @@ class Api::V1::Projects::Contents::BatchCreateService < ApplicationService
end
def excute_data_to_gitea
puts request_body.to_json
@gitea_data = $gitea_client.post_repos_contents_batch_by_owner_repo(owner, repo, {body: request_body.to_json, query: request_params}) rescue nil
raise Error, '创建文件失败!' unless @gitea_data.is_a?(Hash)
raise Error, '提交文件失败!' unless @gitea_data.is_a?(Hash)
end
def check_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params} ) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '分支不存在!' unless result['branch_name'].include?(branch)
raise Error, '分支已存在!' if result['branch_name'].include?(new_branch) && new_branch.nil?
raise Error, '分支已存在!' if result['branch_name'].include?(new_branch) && !new_branch.nil?
end
end

View File

@ -0,0 +1,32 @@
class Api::V1::Projects::Pulls::GetService < ApplicationService
attr_reader :project, :pull_request, :owner, :repo, :index, :token
attr_accessor :gitea_data
def initialize(project, pull_request, token = nil)
@project = project
@pull_request = pull_request
@owner = project&.owner.login
@repo = project&.identifier
@index = pull_request.gitea_number
@token = token
end
def call
load_gitea_data
gitea_data
end
private
def request_params
{
access_token: token
}
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_pulls_by_owner_repo_index(owner, repo, index, {query: request_params})
# raise Error, '获取合并请求失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,48 @@
class Api::V1::Projects::Pulls::Journals::CreateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :pull_request, :issue, :parent_id, :line_code, :note, :commit_id, :path, :type, :diff, :review_id, :user
attr_accessor :journal
validates :type, inclusion: {in: %w(comment problem), message: '请输入正确的Type'}
def initialize(project, pull_request, params, user)
@project = project
@pull_request = pull_request
@issue = pull_request&.issue
@parent_id = params[:parent_id]
@line_code = params[:line_code]
@note = params[:note]
@commit_id = params[:commit_id]
@path = params[:path]
@type = params[:type] || "comment"
@diff = params[:diff]
@review_id = params[:review_id]
@user = user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
check_review_is_exists if review_id.present?
if type == 'problem'
create_problem_journal
else
create_comment_journal
end
journal
end
private
def create_comment_journal
@journal = pull_request.journals.create!(user_id: user&.id, notes: note, parent_id: parent_id, review_id: review_id, commit_id: commit_id, diff: diff, line_code: line_code, path: path)
end
def check_review_is_exists
raise Error, '合并请求审查不存在!' unless @pull_request.reviews.exists?(review_id)
end
def create_problem_journal
@journal = pull_request.journals.create!(user_id: user&.id, notes: note, parent_id: parent_id, review_id: review_id, commit_id: commit_id, diff: diff, line_code: line_code, path: path, need_respond: true)
end
end

View File

@ -0,0 +1,57 @@
class Api::V1::Projects::Pulls::Journals::ListService < ApplicationService
include ActiveModel::Model
attr_reader :project, :pull_request, :keyword, :review_id, :need_respond, :state, :parent_id, :path, :is_full, :sort_by, :sort_direction, :user
attr_accessor :queried_journals
validates :sort_by, inclusion: {in: Journal.column_names, message: '请输入正确的SortBy'}
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}
validates :need_respond, inclusion: {in: [true, false], message: '请输入正确的NeedRespond'}, allow_nil: true
validates :is_full, inclusion: {in: [true, false], message: '请输入正确的IsFull'}
validates :state, inclusion: {in: %w(opened resolved disabled)}, allow_nil: true
def initialize(project, pull_request, params, user)
@project = project
@pull_request = pull_request
@keyword = params[:keyword]
@review_id = params[:review_id]
@need_respond = ActiveModel::Type::Boolean.new.cast(params[:need_respond])
@state = params[:state]
@parent_id = params[:parent_id]
@sort_by = params[:sort_by] || 'created_on'
@sort_direction = params[:sort_direction] || 'asc'
@is_full = ActiveModel::Type::Boolean.new.cast(params[:is_full]) || false
@path = params[:path]
@user = user
end
def call
raise Error, errors.full_messages.join(",") unless valid?
journal_query_data
queried_journals
end
private
def journal_query_data
journals = @pull_request.journals
if !is_full
if parent_id.present?
journals = journals.where(parent_id: parent_id)
else
journals = journals.parent_journals
end
end
journals = journals.where(review_id: review_id) if review_id.present?
journals = journals.where(need_respond: need_respond) if need_respond.present?
journals = journals.where(state: state) if state.present?
journals = journals.where(path: path) if path.present?
q = journals.ransack(notes_cont: keyword)
scope = q.result.includes(:user, :resolveer, :children_journals, review: [:reviewer, pull_request: :issue])
scope = scope.order("journals.#{sort_by} #{sort_direction}")
@queried_journals = scope
end
end

View File

@ -0,0 +1,38 @@
class Api::V1::Projects::Pulls::Journals::UpdateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :pull_request, :journal, :note, :commit_id, :state, :user
attr_accessor :updated_journal
validates :state, inclusion: {in: %w(opened resolved disabled)}
def initialize(project, pull_request, journal, params, user)
@project = project
@pull_request = pull_request
@journal = journal
@note = params[:note]
@commit_id = params[:commit_id]
@state = params[:state]
@user = user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
update_journal
updated_journal
end
private
def update_journal
journal.attributes = {notes: note, commit_id: commit_id, state: state}
if state == 'resolved'
journal.resolve_at = Time.now
journal.resolveer_id = user.id
end
return Error, '保存评论失败!' unless journal.save
@updated_journal = journal.reload
end
end

View File

@ -0,0 +1,48 @@
class Api::V1::Projects::Pulls::ListService < ApplicationService
include ActiveModel::Model
attr_reader :project, :keyword, :status, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :assign_user_id, :sort_by, :sort_direction
attr_accessor :queried_pull_requests
validates :status, inclusion: {in: [0, 1, 2], message: "请输入正确的Status"}, allow_nil: true
validates :sort_by, inclusion: {in: PullRequest.column_names, message: '请输入正确的SortBy'}
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}
def initialize(project, params={})
@project = project
@keyword = params[:keyword]
@status = params[:status].present? ? params[:status].to_i : nil
@priority_id = params[:priority_id]
@issue_tag_id = params[:issue_tag_id]
@version_id = params[:version_id]
@reviewer_id = params[:reviewer_id]
@assign_user_id = params[:assign_user_id]
@sort_by = params[:sort_by] || 'created_at'
@sort_direction = params[:sort_direction] || 'desc'
end
def call
raise Error, errors.full_messages.join(",") unless valid?
pull_request_query_data
queried_pull_requests
end
private
def pull_request_query_data
pull_requests = @project.pull_requests
pull_requests = pull_requests.where(status: status) if status.present?
pull_requests = pull_requests.where(issues: {priority_id: priority_id}) if priority_id.present?
pull_requests = pull_requests.where(issue_tags: {id: issue_tag_id}) if issue_tag_id.present?
pull_requests = pull_requests.where(issues: {fixed_version_id: version_id}) if version_id.present?
pull_requests = pull_requests.where(users: {id: reviewer_id}) if reviewer_id.present?
pull_requests = pull_requests.where(issues: {assigned_to_id: assign_user_id}) if assign_user_id.present?
q = pull_requests.ransack(title_or_body_cont: keyword)
scope = q.result.includes(:fork_project, :journals, :reviews, :reviewers, issue: [:journals, :priority, :version, :issue_tags])
scope = scope.order("pull_requests.#{sort_by} #{sort_direction}")
@queried_pull_requests = scope
end
end

View File

@ -1,4 +1,4 @@
class Api::V1::Projects::PullRequests::Reviews::CreateService < ApplicationService
class Api::V1::Projects::Pulls::Reviews::CreateService < ApplicationService
include ActiveModel::Model
attr_reader :project, :pull_request, :issue, :status, :commit_id, :content, :current_user
@ -20,21 +20,18 @@ class Api::V1::Projects::PullRequests::Reviews::CreateService < ApplicationServi
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
create_review
create_journal
# create_journal
end
return @journal, @review
rescue
raise Error, '服务器错误,请联系系统管理员!'
@review
# rescue
# raise Error, '服务器错误,请联系系统管理员!'
end
private
def create_review
@review = issue.reviews.create!(status: status, content: content, commit_id: commit_id, reviewer_id: @current_user.id)
@review = pull_request.reviews.create!(status: status, content: content, commit_id: commit_id, reviewer_id: @current_user.id)
end
def create_journal
@journal = issue.journals.create!(notes: content, user_id: @current_user.id, review_id: @review.id)
end
end

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