Compare commits

..
192 Commits
Author SHA1 Message Date
HFO4 7a530f5d58 Feat: Color option in dashboard 2019-03-17 21:49:35 +08:00
HFO4 88fac85ca5 Fix: markdown page js file import order 2019-03-17 17:06:01 +08:00
HFO4 9f658e86ec Fix: css name 2019-03-17 16:45:25 +08:00
HFO4 ebee2b09d9 Remove: extra description 2019-03-17 16:35:25 +08:00
HFO4 836cb1a9c7 Remove: Unused import 2019-03-17 16:32:59 +08:00
HFO4 3c7aedc195 Fix: default theme options 2019-03-17 14:48:11 +08:00
HFO4 a11f81966d Feat: Composer install script with fetching static file 2019-03-17 11:43:11 +08:00
HFO4 2c2e8b6052 Modify: Disable cmd install 2019-03-17 11:00:46 +08:00
HFO4 3b7e9298b5 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2019-03-17 10:49:54 +08:00
HFO4 b341d3bc2c Modify: static file in .gitignore 2019-03-17 10:49:27 +08:00
HFO4 fd03047ed2 Delete: static file 2019-03-17 10:48:58 +08:00
HFO4 f15499d274 Fix: Installer missing static file 2019-03-17 09:38:18 +08:00
AaronLiuandGitHub 889b65163b Update README.md 2019-03-16 16:11:07 +08:00
AaronLiuandGitHub 1c05b9a0d3 Update README.md 2019-03-16 16:04:26 +08:00
AaronLiuandGitHub 00a2b452c4 Update README.md 2019-03-16 15:57:09 +08:00
AaronLiuandGitHub 721dad4abe Update README.md 2019-03-16 15:55:08 +08:00
HFO4 ebb95b31c2 Fix: Qiniu thumb error 2019-03-16 15:39:49 +08:00
HFO4 d854913cd2 Fix: Broken context menu in FireFox 2019-03-16 15:23:57 +08:00
HFO4 94c3dd7420 放宽邮箱验证规则 / 七牛缩略图尺寸调整 2019-03-16 14:55:09 +08:00
AaronLiuandGitHub 9d8646c6f6 Merge pull request #143 from HFO4/dev
Fix: broken getUpToken in qiniu policy
2019-03-16 14:22:12 +08:00
HFO4 560e455618 Fix: broken getUpToken in qiniu policy 2019-03-16 14:19:35 +08:00
AaronLiuandGitHub 51f8be8681 Merge pull request #142 from HFO4/dev
Fix: array to string error
2019-03-16 14:13:34 +08:00
AaronLiuandGitHub df8581e238 Delete Download.html 2019-03-16 14:12:51 +08:00
HFO4 bf2f5e621f Fix: array to string error 2019-03-16 14:11:46 +08:00
AaronLiuandGitHub a4439ba210 Merge pull request #141 from HFO4/dev
2.0.0-Alpha1
2019-03-16 14:04:14 +08:00
HFO4 822d9b4bba Add: gziped frontend resources 2019-03-16 14:02:18 +08:00
HFO4 18bcd93d97 Feat: Use official s3 SDK / support minio 2019-03-16 13:29:23 +08:00
HFO4 76ea49890d Feat: Login / Reg / Find pwd form 2019-03-16 10:41:12 +08:00
HFO4 74d0afb441 Feat: 自定义分片大小 2019-03-14 19:37:07 +08:00
HFO4 b2d5547199 Fix: 一些情况下离线下载失败后临时文件无法删除 2019-03-14 19:03:35 +08:00
HFO4 ef636bc2d1 Feat: My share 2019-03-14 11:29:09 +08:00
HFO4 3a4912083c Feat: User profile 2019-03-14 10:00:53 +08:00
HFO4 737665c62c Feat: Change prefer theme color 2019-03-13 19:38:23 +08:00
HFO4 df42ac0e96 Feat: User setting 2019-03-11 21:46:05 +08:00
HFO4 4880972d77 Feat: Referer check 2019-03-11 18:11:19 +08:00
HFO4 84e2ec0f19 Feat: Theme config 2019-03-11 16:21:17 +08:00
HFO4 53c79bfa65 Feat: Single file share 2019-03-11 15:04:35 +08:00
HFO4 41d1294ebf Feat: Folder share 2019-03-10 19:07:01 +08:00
HFO4 d84b00f35d Feat: markdown editor 2019-03-10 13:14:32 +08:00
HFO4 da4f152438 Feat: Video preview 2019-03-10 11:09:20 +08:00
HFO4 3d82929a3b Feat: Remember path 2019-03-09 19:27:25 +08:00
HFO4 2e2357e794 Feat: file type filter &Fix: cannot create torrent remote download in onedrive policy 2019-03-08 21:27:17 +08:00
HFO4 c94e17a4da Fix: img preview wont require height and width anymore 2019-03-07 17:20:18 +08:00
HFO4 c741d79677 Feat: Search file api 2019-03-07 15:29:22 +08:00
HFO4 e0b4b42ec6 Fix: file rename validate 2019-03-06 09:42:21 +08:00
HFO4 65dcedb3ca Fix: blocking while get uptoken 2019-03-05 19:28:55 +08:00
HFO4 422f9d8d6a Feat:initial test 2019-03-03 20:26:25 +08:00
AaronLiuandGitHub 1fc4956a3f Merge pull request #135 from HFO4/revert-104-patch-2
Revert "order 字段名添加分隔符"
2019-02-16 18:15:26 +08:00
AaronLiuandGitHub 564578aefb Revert "order 字段名添加分隔符" 2019-02-16 18:14:38 +08:00
AaronLiuandGitHub 12f98dd0f5 Update README.md 2019-02-16 10:05:29 +08:00
AaronLiuandGitHub c33b6d587f Merge pull request #125 from Yuanuo/patch-04
修复重命名文件时验证失败的问题
2019-01-21 10:18:41 +08:00
Yuanuo 277f882e8d 修复重命名文件时验证失败的问题
文件重命名时,文件后缀名之前的‘.’会导致验证失败,后台做校验时应该先去除后缀名部分。
2019-01-17 14:05:18 +08:00
AaronLiuandGitHub 68399a5877 fix: route checking 2018-12-22 12:23:52 +08:00
AaronLiuandGitHub 6b4f999bc4 fix: route checking 2018-12-22 12:19:56 +08:00
AaronLiuandGitHub 3514ff437c Update README.md 2018-12-19 19:08:36 +08:00
AaronLiuandGitHub 8c344b456d Merge pull request #104 from Instrye/patch-2
order 字段名添加分隔符
2018-11-02 13:34:16 +08:00
InstryeandGitHub ff4fa3f9b2 order 字段名添加分隔符
order方法中的字段名 rank 不会自动添加 `` ,导致在某些MySQL版本中会报错
2018-11-02 11:10:25 +08:00
HFO4 d9fa51a615 fix: move file while forget to change the parent folder ID 2018-10-20 19:21:57 +08:00
HFO4 6dd49fce09 Fix #100 2018-10-20 18:32:00 +08:00
HFO4 ddc0b6ad4b release:1.1.1 2018-10-20 17:08:50 +08:00
HFO4 75cbbcaa10 add: Crontab config reminder 2018-10-20 15:37:20 +08:00
HFO4 060f7a2081 feat: Manage page for task queue 2018-10-20 15:25:35 +08:00
HFO4 a67185c750 fix: installation error in command line 2018-10-19 16:31:51 +08:00
HFO4 38b11c1651 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-10-19 16:30:41 +08:00
HFO4 96b7f4c97d fix: installation error in command line 2018-10-19 16:30:08 +08:00
AaronLiuandGitHub f600b21faa Merge pull request #95 from samnela/change-psr-autoload
moved to psr-4
2018-09-15 19:41:59 +08:00
AaronLiuandGitHub 1693bf800b Merge pull request #88 from Yuanuo/patch-02
PHP basename 中文乱码问题
2018-09-15 19:36:54 +08:00
Samuel NELA f39334125b moved to psr-4 2018-09-14 23:34:33 +02:00
HFO4 9ee144d870 change: Telegram group invite link 2018-09-13 22:16:49 +08:00
HFO4 755fe7331f fix: sql index error 2018-09-13 18:53:37 +08:00
HFO4 78f4a1eae9 fix: failed to close file stream in onedrive policy 2018-09-13 17:07:51 +08:00
HFO4 55ea76e35e fix: Onedrive token encoding error in some case 2018-09-13 16:49:26 +08:00
HFO4 c2a05d3f6c remove: temp file 2018-09-13 15:26:39 +08:00
HFO4 8c43f1fe1c feat: Web ui installer 2018-09-13 15:25:56 +08:00
HFO4 f81a433cbc update: to-do list and function description 2018-09-11 21:05:35 +08:00
HFO4 f2d94ea499 add: policy adding form for Onedrive 2018-09-11 20:42:07 +08:00
HFO4 ca30b650fc add: cron function to refresh token of Onedrive 2018-09-11 19:48:45 +08:00
HFO4 62eca52fe7 add: Onedrive adapter 2018-09-08 17:22:35 +08:00
HFO4 3462e6665b fix: misspelling 2018-09-08 13:53:35 +08:00
HFO4 2b646fb6e3 add: RemoteAdapter 2018-09-08 13:52:26 +08:00
HFO4 e29c94d8d0 add: Onedrive Bussiness SDK 2018-09-08 13:25:21 +08:00
HFO4 044ece0b80 add: S3Adapter & OssAdapter 2018-09-08 13:15:04 +08:00
HFO4 195d747c5d fix: error displaying capacity unit while editing user group 2018-09-07 20:48:13 +08:00
HFO4 16fe5d7f7d add: OssAdapter 2018-09-07 20:36:44 +08:00
HFO4 732d04c0a7 add QiniuAdapter 2018-09-07 19:46:54 +08:00
HFO4 6593cb7de9 add: LocalAdapter 2018-09-06 19:26:23 +08:00
HFO4 7a34a5ffb8 add: Onedrive large file uploading 2018-09-06 19:07:17 +08:00
HFO4 e989aa47d9 add: Single Onedrive reUpload handle function 2018-09-05 19:19:24 +08:00
HFO4 159ee86b60 add: Task model and database table 2018-09-05 16:43:52 +08:00
HFO4 61910ccf35 add: Onedrive 2018-09-05 15:55:51 +08:00
HFO4 1fe7be12c6 Fix: #67 2018-08-30 20:02:59 +08:00
HFO4 72ab679dd5 Fix #86 2018-08-30 18:27:40 +08:00
AaronLiuandGitHub 946408b642 Update composer.json 2018-08-20 18:19:04 +08:00
AaronLiuandGitHub 1d5f742ba7 Delete composer.lock 2018-08-20 18:12:26 +08:00
AaronLiuandGitHub 84c6e1a74c Update README.md 2018-08-20 18:11:28 +08:00
AaronLiuandGitHub a4cef20c01 Merge pull request #89 from Yuanuo/patch-03
修复X-Sendfile模式时附件文件名和Content-Type异常的问题。
2018-08-19 22:43:22 +08:00
AaronLiuandGitHub 5faea8f5ae Merge pull request #87 from Yuanuo/patch-01
分享文件列表根据分享时间排序而不应该是ID。
2018-08-19 22:42:28 +08:00
Yuanuo 899ee59c73 修复X-Sendfile模式时附件文件名和Content-Type异常的问题。
1,针对各浏览器的附件文件名的不同头信息格式。
2,解决Content-Type为text/html的问题。
2018-08-04 14:49:47 +08:00
Yuanuo 2807c1c79e PHP basename 中文乱码问题
php basename()无法获取带中文字符的文件名(乱码)
2018-08-04 14:08:59 +08:00
Yuanuo d5f5cbc05b 分享文件列表根据分享时间排序而不应该是ID。
显示分享文件列表时根据分享时间倒序排列,最新分享的文件显示在最前,因为分享顺序并不一定按照文件上传顺序决定。
2018-08-04 14:00:25 +08:00
HFO4 a1a9f7cdb0 complete thumb preivew in folder shareing page 2018-07-01 21:45:02 +08:00
HFO4 c7f7e20978 add thumb preview in folder sharing page 2018-06-30 19:44:24 +08:00
HFO4 c11710325d Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-06-30 19:28:45 +08:00
AaronLiuandGitHub a3599b399d Update README.md 2018-05-17 20:42:11 +08:00
AaronLiuandGitHub 8992627c48 Update README.md 2018-05-17 20:41:28 +08:00
HFO4 07fef77c4c 目录分享侧边连 2018-05-17 16:18:08 +08:00
HFO4 81abe89e84 修复部分情况下离线下载未完成被判定为已完成 2018-04-26 21:35:19 +08:00
HFO4 514ad474ea fix index error in admin panel 2018-04-22 22:11:06 +08:00
HFO4 b33df3da72 fix templates fliename error 2018-04-20 11:43:17 +08:00
HFO4 fd8fc70b07 fix templates fliename error 2018-04-20 11:41:09 +08:00
HFO4 91c9870f49 fix aria2 array index error 2018-04-19 23:18:02 +08:00
AaronLiuandGitHub f93edcc82b Merge pull request #65 from HFO4/dev
Dev
2018-04-19 19:46:10 +08:00
HFO4 b1f2774d41 turn off debug 2018-04-19 19:45:42 +08:00
HFO4 487f4e3139 fix maximum 2GB storage 2018-04-19 19:44:22 +08:00
AaronLiuandGitHub 9651caa219 Merge pull request #64 from HFO4/dev
remote download complete
2018-04-19 19:09:22 +08:00
HFO4 f5d69ebdae turn off debug switch 2018-04-19 19:08:06 +08:00
HFO4 36e6526259 complete remote download based on aria2 2018-04-19 19:07:22 +08:00
HFO4 334d25833c Fix #62 2018-04-18 20:40:56 +08:00
HFO4 412541a540 add list for completed remote download and fix storage check before starting downloading 2018-04-18 20:31:24 +08:00
HFO4 3326c9232e add download task list and optimize temp file recovery 2018-04-17 19:59:13 +08:00
HFO4 6d9b67ac20 add torrent download support 2018-04-16 20:47:48 +08:00
HFO4 ccbb6c7778 种子离线下载前端 2018-04-10 22:28:17 +08:00
HFO4 b8b63662ea 离线下载添加URL交互界面 2018-04-09 22:04:56 +08:00
HFO4 1b87365b8a aria2 种子下载及多文件处理 2018-04-07 15:16:57 +08:00
HFO4 ee56919747 aria2下载失败异常处理 2018-04-07 11:02:43 +08:00
AaronLiuandGitHub 4877a8e292 Merge pull request #58 from HFO4/dev
Merge Dev2
2018-04-06 21:29:21 +08:00
HFO4 1af411daee 关闭Debug,修改错误提示 2018-04-06 21:28:48 +08:00
HFO4 c4ecee4897 关闭Debug,修改错误提示 2018-04-06 21:24:56 +08:00
HFO4 f4a4ca02b5 aria2下载完成后的处理 2018-04-06 21:09:24 +08:00
AaronLiuandGitHub f3ff61cbc6 Merge pull request #56 from HFO4/dev
Merge Dev 01
2018-04-06 16:26:05 +08:00
HFO4 aabc32efb5 aria2状态查询 2018-04-06 16:21:40 +08:00
HFO4 126aacca63 离线下载请求及数据库记录 2018-04-06 10:26:10 +08:00
HFO4 2ade8861c9 aria2下载请求完成,并且修复一个远程服务端path的bug 2018-04-05 17:07:36 +08:00
HFO4 32ca1132f2 aria2初始化 2018-04-05 13:24:52 +08:00
HFO4 7536c8695b 更新remote类型接口URL 2018-03-29 21:41:40 +08:00
HFO4 622b80a680 开启Debug】 2018-03-29 21:39:04 +08:00
HFO4 0fe4e8a8db 发送测试邮件返回更多Debug信息(Fix #46) 2018-03-24 22:25:30 +08:00
HFO4 37b4357435 修复阿里云OSS公共空间无法下载文件 2018-03-23 18:23:12 +08:00
HFO4 45ea9404ba Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-03-11 19:22:47 +08:00
HFO4 764fdd7e36 优化移动端导航效果 2018-03-11 19:22:29 +08:00
AaronLiuandGitHub 219d894a08 Update README.md 2018-03-11 12:50:18 +08:00
HFO4 7d5e8fb2a1 1.0.3 2018-03-11 11:42:38 +08:00
HFO4 956deac89f 数据库升级逻辑 2018-03-11 11:39:33 +08:00
HFO4 4964f53dac 视频预览支持弹幕 2018-03-09 21:01:40 +08:00
AaronLiuandGitHub 037e6ad0b3 Update README.md 2018-03-08 22:44:06 +08:00
AaronLiuandGitHub 9c7494256c Update README.md 2018-03-08 22:43:18 +08:00
HFO4 21518300b4 定义移动端导航栏主题色 2018-03-07 20:07:25 +08:00
HFO4 99759b5a77 Fix #35 2018-03-05 19:20:12 +08:00
HFO4 d58b824d8b Fix #32 2018-03-05 19:09:19 +08:00
HFO4 33e56ca287 Merge branch 'master' of https://github.com/HFO4/Cloudreve 2018-03-05 19:03:37 +08:00
HFO4 c7901d55f0 修复 #25 2018-03-05 19:03:11 +08:00
AaronLiuandGitHub 53d01e5f6e Merge pull request #34 from Instrye/patch-1
修复 JS 获取 change 错误
2018-03-01 17:35:41 +08:00
InstryeandGitHub 4b58719e7c 修复 JS 获取 change 错误 2018-03-01 17:24:40 +08:00
AaronLiuandGitHub a82bee0216 Merge pull request #33 from Instrye/patch-1
修复 Admin删除分享文件的JS错误
2018-03-01 17:07:23 +08:00
InstryeandGitHub 0ea0902059 修复 Admi n删除分享文件的JS错误
修复 JQuery 获取 delete 对象错误
2018-03-01 17:05:05 +08:00
HFO4 a57dd79319 添加oss策略时加上http的提示 2018-02-19 17:55:16 +08:00
HFO4 228b898e9d 登录状态下访问首页跳转到文件页 2018-02-19 15:51:37 +08:00
HFO4 8f8b9df5a5 补齐.htaccess 2018-02-19 13:00:52 +08:00
HFO4 36925a63ef 删除锁文件 2018-02-18 21:51:00 +08:00
HFO4 df03461892 更新readme 2018-02-18 21:49:07 +08:00
HFO4 6361d4bd2e 修复二步验证二维码错误 2018-02-18 21:47:15 +08:00
AaronLiuandGitHub 937dfc77fd Merge pull request #18 from LinEvil/patch-1
Update TwoFactor.php
2018-02-18 21:34:43 +08:00
LinEvilandGitHub 31f1eb839f Update TwoFactor.php
安装的依赖endroid/qrcode已经弃用
请使用composer require endroid/qr-code
2018-02-18 21:32:18 +08:00
HFO4 12f7f691eb 修复因目录重名无法创建目录 2018-02-18 21:08:19 +08:00
HFO4 2d183acd0d 测试自动构建 2018-02-18 19:46:43 +08:00
HFO4 363da22bda 测试自动构建 2018-02-18 19:42:12 +08:00
HFO4 28d8d02508 测试自动构建 2018-02-18 19:39:50 +08:00
HFO4 662c3019d4 更新deploy.sh 2018-02-18 19:24:14 +08:00
HFO4 f5502b3666 添加自动打包 2018-02-18 19:20:06 +08:00
HFO4 4e550dcdb2 修复nginx无法使用sendfile 2018-02-16 11:59:09 +08:00
HFO4 59f8a9b58d 更新数据库版本 2018-02-16 10:46:07 +08:00
HFO4 b0898fcab4 支持通过sendfile下载文件 2018-02-16 10:31:53 +08:00
HFO4 8c8561dc31 拼写错误 2018-02-15 21:12:24 +08:00
HFO4 141de8ba54 修复部分情况加WebDAV无法使用 2018-02-15 21:08:26 +08:00
HFO4 159098decf 初步添加sendfile支持 2018-02-15 20:28:25 +08:00
HFO4 24789ca8a9 更新版本号 2018-02-14 18:11:57 +08:00
HFO4 544769bf04 Fix #12 2018-02-13 17:20:36 +08:00
HFO4 85119bc54d 新增发送安装反馈 2018-02-13 16:44:51 +08:00
HFO4 d3061f0526 README特性更新 2018-02-13 16:31:21 +08:00
HFO4 4492991bbb 修复一处url错误 2018-02-13 16:28:19 +08:00
HFO4 1e79428425 新增远程上传策略 2018-02-13 16:11:15 +08:00
HFO4 c28f08c210 自定远程服务端(未完成) 2018-02-10 14:14:40 +08:00
rainays 0e0f7d5258 添加登录按钮 2018-02-08 22:18:10 +08:00
HFO4 d2ca69b945 视频预览使用Dplayer 2018-02-07 14:40:46 +08:00
HFO4 9cb572f0da clear cache 2018-02-06 18:56:51 +08:00
HFO4 9994f12aa8 clear cache 2018-02-06 18:56:09 +08:00
HFO4 20b7d67adc 修复目录分享无法使用 2018-02-06 18:55:04 +08:00
HFO4 63cc404d74 修复目录分享无法使用 2018-02-06 18:54:08 +08:00
HFO4 47f334b6f4 修复部分服务器无法收到回调Authorization头 2018-02-06 17:34:49 +08:00
HFO4 1431f433d1 修复首页背景适应 2018-02-06 17:08:16 +08:00
HFO4 b6c1382e73 修复某些情况下验证码无效 2018-02-05 16:28:30 +08:00
HFO4 02ce111717 修复部分情况下安装报错 2018-02-05 13:59:19 +08:00
698 changed files with 76703 additions and 27338 deletions
+5
View File
@@ -0,0 +1,5 @@
*.map
composer.lock
*.chunk.js
*.chunk.css
runtime~*.js
+1
View File
@@ -6,3 +6,4 @@
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+7
View File
@@ -0,0 +1,7 @@
language: php
php:
- 7.1
before_script:
- composer install
- chmod +x ./deploy.sh
script: ./deploy.sh
+63 -8
View File
@@ -16,10 +16,11 @@ class Installer{
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver $version
Ver $version
================================================
";
$ioContext->write($welcomMsg);
self::prepareStatic($event);
$sqlInfo = self::getSqlInformation($event);
$ioContext->write("");
$siteUrl=$ioContext->ask("The full-url to access to your Cloudreve (e.g. https://pan.aoaoao.me/ , 'http' must be included in the front and '/' must be included at the end):");
@@ -60,10 +61,60 @@ class Installer{
$ioContext->write("IMPORTANT! You may still have to configure the URL Rewrite to set everthing to work.");
$ioContext->write("Refer to the install manual for more informatioin.");
$ioContext->write("=======================");
self::sendFeedBack($siteUrl);
}
public static function prepareStatic(Event $event){
$ioContext = $event->getIO();
$ioContext->write("Fetching static files from cloudreve.org...");
if(!is_writable("runtime") || !is_writable("static")){
$ioContext->writeError("[Error] 'runtime' or 'static' directory is not writable. To retry, run 'composer install'");
exit();
}
if(file_exists("runtime/static.zip")){
$ioContext->writeError("[Error] 'runtime/static.zip' already exist. To retry, run 'composer install'");
exit();
}
if(file_exists("runtime/static")){
$ioContext->writeError("[Error] directory 'runtime/static' already exist. To retry, run 'composer install'");
exit();
}
try{
file_put_contents("runtime/static.zip",file_get_contents("https://cloudreve.org/download.php?newestStatic"));
}catch (Exception $e) {
$ioContext->writeError("[Error] Failed to download static file. To retry, run 'composer install'");
exit();
}
$ioContext->write("Unzip static files...");
$zip = new \ZipArchive;
$res = $zip->open('runtime/static.zip');
if ($res === TRUE) {
$zip->extractTo('runtime/static/');
$zip->close();
}else {
$ioContext->writeError("[Error] Failed to open runtime/static.zip. To retry, run 'composer install'");
exit();
}
self::recurse_copy("runtime/static/build/static", "static");
}
public static function recurse_copy($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
self::recurse_copy($src . '/' . $file,$dst . '/' . $file);
}else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
public static function writrConfig(Event $event,$sqlInfo){
$ioContext = $event->getIO();
$ioContext = $event->getIO();
try {
$fileContent = file_get_contents("CloudreveInstaller/database_sample.php");
$replacement = array(
@@ -86,7 +137,7 @@ class Installer{
$hostname=$ioContext->ask("Input the hostname of your MySQL server (Default:127.0.0.1):","127.0.0.1");
$database=$ioContext->ask("The database name:","127.0.0.1");
$username=$ioContext->ask("The username of your MySQL server (Default:root):","root");
$password=$ioContext->askAndHideAnswer("The password of your MySQL server:");
$password=$ioContext->ask("The password of your MySQL server:");
$hostport=$ioContext->ask("The hostport of your MySQL server (Default:3306):","3306");
$mysqli = @new \mysqli($hostname, $username, $password, $database, $hostport);
if ($mysqli->connect_error) {
@@ -97,13 +148,17 @@ class Installer{
return self::getSqlInformation($event);
}
return [
"hostname" => $hostname,
"database" => $database,
"username" => $username,
"password" => $password,
"hostport" => $hostport,
"hostname" => $hostname,
"database" => $database,
"username" => $username,
"password" => $password,
"hostport" => $hostport,
];
}
public static function sendFeedBack($url){
@file_get_contents("http://toy.aoaoao.me/feedback.php?url=".urlencode($url));
}
}
?>
File diff suppressed because one or more lines are too long
+11 -6
View File
@@ -6,14 +6,18 @@ Cloudreve - Make the cloud easy for everyone
[![Latest Unstable Version](https://poser.pugx.org/hfo4/cloudreve/v/unstable)](https://packagist.org/packages/hfo4/cloudreve)
[![License](https://poser.pugx.org/hfo4/cloudreve/license)](https://packagist.org/packages/hfo4/cloudreve)
基于ThinkPHP构建的网盘系统,能够助您以较低成本快速搭建起公私兼备的网盘。
[主页](https://cloudreve.org) | [论坛](https://forum.cloudreve.org) | [演示站](https://drive.aoaoao.me) | [QQ群](https://jq.qq.com/?_wv=1027&k=5TX6sJY)
![homepage.png](https://download.aoaoao.me/homepage.png)
使用ThinkPHP + React + Redux + Material-UI构建的网盘系统,能够助您以较低成本快速搭建起公私兼备的网盘。
![HomePage](https://i.loli.net/2019/03/16/5c8caf825282a.jpg)
目前已经实现的特性:
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3,当然,还有本地存储
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3、Onedrive、自建远程服务器,当然,还有本地存储
* 可限制单文件最大大小、MIMEType、文件后缀、用户可用容量
* 自定义主题配色
* 基于Aria2的离线下载
* 图片、音频、视频、文本、Markdown、Ofiice文档 在线预览
* 移动端全站响应式布局
* 文件、目录分享系统,可创建私有分享或公开分享链接
@@ -22,9 +26,10 @@ Cloudreve - Make the cloud easy for everyone
* 初步完善的后台,方便管理
* 拖拽上传、分片上传、断点续传、下载限速(*实验性功能)
* 多上传策略,可为不同用户组分配不同策略
* 用户组基础权限设置
* 用户组基础权限设置、二步验证
* WebDAV协议支持
安装需求
------------
* LNMP/AMP With PHP5.6+
@@ -46,7 +51,7 @@ $ composer create-project hfo4/cloudreve:dev-master
/ __\ | ___ _ _ __| |_ __ _____ _____
/ / | |/ _ \| | | |/ _` | '__/ _ \ \ / / _ \
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver XX
================================================
@@ -88,7 +93,7 @@ location / {
* 到 设置-基础设置 中更改站点URL,如果不更改,程序无法正常接受回调请求
* 添加Crontab定时任务 :你的域名/Cron
* 如果你打算使用本地上传策略并且不准备开启外链功能,请将·public/uploads·目录设置为禁止外部访问
* 如需启用二步验证功能,请依次执行`composer require phpgangsta/googleauthenticator:dev-master` `composer require endroid/qrcode`安装二步验证支持库
* 如需启用二步验证功能,请依次执行`composer require phpgangsta/googleauthenticator:dev-master` `composer require endroid/qr-code`安装二步验证支持库
* 给本项目一个Star~
文档
+3 -1
View File
@@ -9,4 +9,6 @@
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
return [];
return [
'app\index\command\Task',
];
+8 -2
View File
@@ -18,7 +18,7 @@ return [
// 应用命名空间
'app_namespace' => 'app',
// 应用调试模式
'app_debug' => false,
'app_debug' => true,
// 应用Trace
'app_trace' => false,
// 应用模式状态
@@ -156,7 +156,7 @@ return [
'exception_tmpl' => THINK_PATH . 'tpl' . DS . 'think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
'error_message' => '很抱歉,出现错误 :(',
// 显示错误信息
'show_error_msg' => false,
// 异常处理handle类 留空使用 \think\exception\Handle
@@ -240,6 +240,12 @@ return [
'var_page' => 'page',
'list_rows' => 15,
],
//分片上传大小(只对本地策略有效)
'upload'=>[
'chunk_size' => 10*1024*1024,
],
// +----------------------------------------------------------------------
// | 验证码设置
// +----------------------------------------------------------------------
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\index\command;
use think\Db;
use think\console\Command;
use think\console\Input;
use think\console\Output;
class Task extends Command
{
const SLEEP_TIME = 1;
protected function configure()
{
$this->setName('run')->setDescription('Start processing tasks for Cloudreve');
}
protected function Init(Output $output){
$output->writeln("Cloudreve tasks processor started.");
}
protected function setComplete($taskId,Output $output){
$output->writeln("Cloudreve tasks processor started.");
}
protected function execute(Input $input, Output $output)
{
self::Init($output);
while (1){
$newTaskInfo = Db::name("task")->where("status","todo")->find();
if(empty($newTaskInfo)){
sleep(self::SLEEP_TIME);
continue;
}
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"processing"]);
$output->writeln("[New task] Name:".$newTaskInfo["task_name"]." Type:".$newTaskInfo["type"]);
$task = new \app\index\model\Task();
$task->taskModel = $newTaskInfo;
$task->input = $input;
$task->output = $output;
$task->Doit();
if($task->status=="error"){
$output->writeln("[Error] ".$task->errorMsg);
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"error|".$task->errorMsg]);
}else{
$output->writeln("[Complete]");
Db::name("task")->where("id",$newTaskInfo["id"])->update(["status"=>"complete"]);
}
}
}
}
?>
+124 -6
View File
@@ -29,12 +29,50 @@ class Admin extends Controller{
}
public function index(){
if($this->adminObj->checkDbVersion()){
$this->redirect(url('/Admin/UpdateDb','',''));
exit();
}
if($this->adminObj->checkCron()){
$isCronOk = true;
}else{
$isCronOk = false;
}
return view('admin_index', [
'options' => $this->siteOptions,
'statics' => $this->adminObj->getStatics(),
'isCronOk' => $isCronOk,
]);
}
public function UpdateDb(){
if(!$this->adminObj->checkDbVersion()){
$this->redirect(url('/Admin','',''));
exit();
}
echo "<meta charset='utf-8'>";
echo "准备升级数据库,当前数据库版本:".$this->adminObj->dbVerInfo["now"];
echo ", 目标数据库版本:".$this->adminObj->dbVerInfo["require"].";<br>";
$updatePath = ROOT_PATH . "update_".$this->adminObj->dbVerInfo["now"]."to".$this->adminObj->dbVerInfo["require"].".sql";
if(!file_exists($updatePath)){
die("数据库更新文件(".$updatePath.")不存在,升级中止.");
}
echo "获取升级SQL文件(".$updatePath.")<br>";
$updateContent = file_get_contents($updatePath);
echo "将执行以下指令:<br>";
echo "<code>".htmlspecialchars($updateContent)."</code><br>";
$sqlSingle = explode(";", $updateContent);
foreach ($sqlSingle as $key => $value){
if(empty($value)){
continue;
}
if(!Db::execute($value)){
echo "<strong>执行$value 时出现错误</strong><br>";
}
}
echo "<strong>升级完成,<a href='/Admin'>返回管理面板</a></strong>";
}
public function Setting(){
return view('basic_setting', [
'options' => $this->siteOptions,
@@ -64,6 +102,15 @@ class Admin extends Controller{
]);
}
public function Color(){
$options = Option::getValues(["basic"]);
return view('color', [
'options' => $this->siteOptions,
'optionsForSet' => $options,
'colors'=> \json_decode($options["themes"],true)
]);
}
public function Theme(){
$fileName=input("?param.name") ? input("param.name") : "error";
$dir = ROOT_PATH."application/index/view/";
@@ -103,6 +150,10 @@ class Admin extends Controller{
return $this->adminObj->saveThemeFile(input('post.'));
}
public function SaveColorSetting(){
return $this->adminObj->saveColorSetting(input('post.'));
}
public function SettingMail(){
return view('mail_setting', [
'options' => $this->siteOptions,
@@ -120,7 +171,7 @@ class Admin extends Controller{
public function SettingOther(){
return view('other_setting', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["file_edit","share","avatar","admin","storage_policy"]),
'optionsForSet' => Option::getValues(["file_edit","share","avatar","admin","storage_policy","download"]),
]);
}
@@ -216,6 +267,14 @@ class Admin extends Controller{
return $this->adminObj->saveMailSetting(input('post.'));
}
public function SaveAria2Setting(){
return $this->adminObj->saveAria2Setting(input('post.'));
}
public function SaveTaskOption(){
return $this->adminObj->saveTaskOption(input('post.'));
}
public function SendTestMail(){
return $this->adminObj->sendTestMail(input('post.'));
}
@@ -298,7 +357,7 @@ class Admin extends Controller{
}
public function DeleteShareMultiple(){
return $this->adminObj->deleteShare(json_decode(input('post.id'),true));
return $this->adminObj->deleteShare(\json_decode(input('post.id'),true));
}
public function DeleteMultiple(){
@@ -334,7 +393,7 @@ class Admin extends Controller{
}
public function DeleteUsers(){
$uidGroup = json_decode(input('post.id'),true);
$uidGroup = \json_decode(input('post.id'),true);
foreach ($uidGroup as $key => $value) {
$this->adminObj->deleteUser($value,$this->userObj->uid);
}
@@ -409,8 +468,20 @@ class Admin extends Controller{
]);
}
public function PolicyAddRemote(){
return view('add_policy_remote', [
'options' => $this->siteOptions,
]);
}
public function PolicyAddOnedrive(){
return view('add_policy_onedrive', [
'options' => $this->siteOptions,
]);
}
public function About(){
$verison = json_decode(file_get_contents(ROOT_PATH . "application/version.json"),true);
$verison = \json_decode(file_get_contents(ROOT_PATH . "application/version.json"),true);
return view('about', [
'options' => $this->siteOptions,
'programVersion' => $verison,
@@ -419,15 +490,24 @@ class Admin extends Controller{
}
public function Purchase(){
$packData = json_decode(Option::getValue("pack_data"),true);
$packData = \json_decode(Option::getValue("pack_data"),true);
return view('purchase', [
'options' => $this->siteOptions,
'pack' => $packData,
]);
}
public function Queue(){
$taskOption = Option::getValue("task_queue_token");
return view('task', [
'options' => $this->siteOptions,
'taskOption' => $taskOption,
'task' => $this->adminObj->getTasks(),
]);
}
public function PurchaseGroup(){
$groupData = json_decode(Option::getValue("group_sell_data"),true);
$groupData = \json_decode(Option::getValue("group_sell_data"),true);
foreach ($groupData as $key => $value) {
$groupData[$key]["group"] = Db::name("groups")->where("id",$value["goup_id"])->find();
}
@@ -444,5 +524,43 @@ class Admin extends Controller{
'policy' => $this->adminObj->getAvaliablePolicy(),
]);
}
public function RemoteDownload(){
$this->adminObj->listDownloads();
return view('download', [
'options' => $this->siteOptions,
'optionsForSet' => Option::getValues(["aria2"]),
'list' => $this->adminObj->pageData,
'originList' => $this->adminObj->listData,
'pageNow' => $this->adminObj->pageNow,
'pageTotal' => $this->adminObj->pageTotal,
'dataTotal' => $this->adminObj->dataTotal,
]);
}
public function CancelDownload(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new \app\index\model\Aria2($aria2Options);
$downloadItem = Db::name("download")->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
public function UpdateOnedriveToken(){
$policyId = input("get.id");
$this->adminObj->updateOnedriveToken($policyId);
}
public function OneDriveCalllback(){
$code = input("get.code");
$this->adminObj->oneDriveCalllback($code);
}
}
@@ -81,4 +81,11 @@ class Callback extends Controller{
$handllerObj -> s3Handler($callbackKey);
}
public function Remote(){
ob_end_clean();
header('Content-Type: application/json');
$handllerObj = new CallbackHandler(file_get_contents("php://input"));
$handllerObj -> remoteHandler(Request::instance()->header('Authorization'));
}
}
+11 -19
View File
@@ -14,31 +14,24 @@ class Explore extends Controller{
public $siteOptions;
public function _initialize(){
$this->siteOptions = Option::getValues(["basic"]);
}
public function Search(){
$this->visitorObj = new User(cookie('user_id'),cookie('login_key'));
return view("search",[
"options" => $this->siteOptions,
'loginStatus' => $this->visitorObj->loginStatus,
'userData' => $this->visitorObj->userSQLData,
]);
}
public function S(){
$this->visitorObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic"],$this->visitorObj->userSQLData);
$keyWords=input("param.key");
if(empty($keyWords)){
$this->redirect('/Explore/Search',302);
}
$list = Db::name('shares')
$this->error("搜索词不为空",200,$this->siteOptions);
}else{
$list = Db::name('shares')
->where('type',"public")
->where('origin_name',"like","%".$keyWords."%")
->order('id DESC')
->paginate(10);
$listData = $list->all();
->order('share_time DESC')
->select();
}
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = $value["origin_name"];
@@ -50,9 +43,8 @@ class Explore extends Controller{
return view("result",[
"options" => $this->siteOptions,
'loginStatus' => $this->visitorObj->loginStatus,
'userData' => $this->visitorObj->userSQLData,
'list' => $listData,
'listOrigin' => $list,
'userData' => $this->visitorObj->getInfo(),
'list' => json_encode($listData),
'keyWords' => $keyWords,
]);
}
+25 -26
View File
@@ -27,37 +27,33 @@ class File extends Controller{
}
}
/**
* [index description]
* @Author Aaron
* @DateTime 2017-07-03
* @return [type] [description]
*/
public function index(){
return "";
}
/**
* [List description]
* @Author Aaron
* @DateTime 2017-07-03
*/
public function ListFile(){
$reqPath = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
return FileManage::ListFile($reqPath,$this->userObj->uid);
return json(FileManage::ListFile($reqPath,$this->userObj->uid));
}
public function SearchFile(){
$keyWords = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
return json(FileManage::searchFile($keyWords,$this->userObj->uid));
}
public function Delete(){
$reqPath = json_decode(file_get_contents("php://input"),true)['items'];
$dirPath = json_decode(file_get_contents("php://input"),true)['dirs'];
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = array_key_exists("dirs",$reqData)?$reqData["items"]:array();
$dirPath = array_key_exists("dirs",$reqData)?$reqData["dirs"]:array();
FileManage::DirDeleteHandler($dirPath,$this->userObj->uid);
return FileManage::DeleteHandler($reqPath,$this->userObj->uid);
return json(FileManage::DeleteHandler($reqPath,$this->userObj->uid));
}
public function Move(){
$reqPath = json_decode(file_get_contents("php://input"),true)['items'];
$dirPath = json_decode(file_get_contents("php://input"),true)['dirs'];
$newPath = json_decode(file_get_contents("php://input"),true)['newPath'];
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = array_key_exists("dirs",$reqData)?$reqData["items"]:array();
$dirPath = array_key_exists("dirs",$reqData)?$reqData["dirs"]:array();
$newPath = $reqData['newPath'];
return FileManage::MoveHandler($reqPath,$dirPath,$newPath,$this->userObj->uid);
}
@@ -68,7 +64,7 @@ class File extends Controller{
}
public function Preview(){
$reqPath = $_GET["path"];
$reqPath =$_GET["path"];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$Redirect = $fileObj->PreviewHandler();
if($Redirect[0]){
@@ -78,7 +74,7 @@ class File extends Controller{
public function ListPic(){
$reqPath = $_GET["path"];
return FileManage::listPic($reqPath,$this->userObj->uid);
return json(FileManage::listPic($reqPath,$this->userObj->uid));
}
public function Download(){
@@ -91,19 +87,22 @@ class File extends Controller{
}
public function Share(){
$reqPath = json_decode(file_get_contents("php://input"),true)['item'];
$shareType = json_decode(file_get_contents("php://input"),true)['shareType'];
ShareHandler::createShare($reqPath,$shareType,$this->userObj->getSQLData(),$this->userObj->getGroupData());
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = $reqData['item'];
$shareType = $reqData['shareType'];
$sharePwd = $reqData['pwd'];
ShareHandler::createShare($reqPath,$shareType,$sharePwd,$this->userObj->getSQLData(),$this->userObj->getGroupData());
}
public function gerSource(){
$reqPath = $_POST["path"];
$reqData = json_decode(file_get_contents("php://input"),true);
$reqPath = $reqData['path'];
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->Source();
}
public function Content(){
$reqPath = json_decode(file_get_contents("php://input"),true)['item'];
$reqPath = urldecode(input("get.path"));
$fileObj = new FileManage($reqPath,$this->userObj->uid);
$FileHandler = $fileObj->getContent();
}
@@ -170,6 +169,6 @@ class File extends Controller{
}
}
return FileManage::createFolder($dirName,$dirPosition,$this->userObj->uid);
return json(FileManage::createFolder($dirName,$dirPosition,$this->userObj->uid));
}
}
+8 -30
View File
@@ -30,45 +30,23 @@ class Home extends Controller{
}
$policyData["max_size"] = $policyData["max_size"]/(1024*1024);
return view('home', [
'options' => Option::getValues(['basic','upload']),
'options' => Option::getValues(['basic','upload'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'extLimit' => $extLimit,
'policyData' => $policyData,
'groupData' => $groupData,
'chunkSize' => config('upload.chunk_size'),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}
public function Album(){
public function Download(){
$userInfo = $this->userObj->getInfo();
$list = Db::name("files")->where("upload_user",$this->userObj->uid)
->where(function ($query) {
$query->where('orign_name', "like","%jpg")
->whereor('orign_name', "like","%png")
->whereor('orign_name', "like","%gif")
->whereor('orign_name', "like","%bmp");
})
->order('id DESC')
->paginate(9);
$pageCount = ceil(Db::name("files")->where("upload_user",$this->userObj->uid)
->where(function ($query) {
$query->where('orign_name', "like","%jpg")
->whereor('orign_name', "like","%png")
->whereor('orign_name', "like","%gif")
->whereor('orign_name', "like","%bmp");
})
->order('id DESC')->count()/9);
$listData = $list->all();
$pageNow = input("?get.page")?input("get.page"):1;
if($pageNow>$pageCount){
$this->error('页面不存在',404,Option::getValues(['basic','group_sell']));
}
return view('album', [
'options' => Option::getValues(['basic','group_sell']),
$groupData = $this->userObj->getGroupData();
return view('download', [
'options' => Option::getValues(['basic','group_sell'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'list' => $listData,
'listOrigin' => $list,
'pageCount' => $pageCount,
'page' => $pageNow,
'groupData' => $groupData,
]);
}
+4
View File
@@ -13,6 +13,10 @@ class Index extends Controller{
public function index(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if($this->userObj->loginStatus){
$this->redirect(url('/Home','',''));
exit();
}
$userInfo = $this->userObj->getInfo();
return view('index', [
'options' => Option::getValues(['basic']),
+49 -22
View File
@@ -14,17 +14,11 @@ class Member extends Controller{
public $userObj;
/**
* [index description]
* @return [type] [description]
*/
public function index(){
echo "hello";
echo "Pong";
}
/**
* [Register description]
*/
public function Register(){
if(input('?post.username-reg') && input('?post.password-reg')){
$regAction = User::register(input('post.username-reg'),input('post.password-reg'),input('post.captchaCode'));
@@ -51,9 +45,6 @@ class Member extends Controller{
}
}
/**
* [Login description]
*/
public function Login(){
if(input('?post.userMail') && input('?post.userPass')){
$logAction = User::login(input('post.userMail'),input('post.userPass'),input('post.captchaCode'));
@@ -67,9 +58,6 @@ class Member extends Controller{
}
}
/**
* [LogOut description]
*/
public function LogOut(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->userObj->clear();
@@ -82,13 +70,36 @@ class Member extends Controller{
$this->userObj->getMemory();
}
public function SignUp(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "register",
]);
}
public function FindPwd(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "resetPwd",
]);
}
public function LoginForm(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('login', [
'options' => Option::getValues(['basic']),
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "login",
]);
}
@@ -114,10 +125,11 @@ class Member extends Controller{
public function TwoStep(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
return view('two_step', [
'options' => Option::getValues(['basic']),
return view('login', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'RegOptions' => Option::getValues(['register','login']),
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "TwoStep",
]);
}
@@ -133,14 +145,16 @@ class Member extends Controller{
public function emailActivate(){
$activationKey = input('param.key');
$basicOptions = Option::getValues(['basic']);
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->isLoginStatusCheck();
$basicOptions = Option::getValues(['basic','register','login'],$this->userObj->userSQLData);
$activeAction = User::activicateUser($activationKey);
if($activeAction[0]){
return view('active_user', [
return view('login', [
'options' => $basicOptions,
'RegOptions' => $basicOptions,
'loginStatus' => $this->userObj->loginStatus,
'pageId' => "emailActivate",
]);
}else{
$this->error($activeAction[1],403,$basicOptions);
@@ -150,15 +164,17 @@ class Member extends Controller{
public function resetPwd(){
$resetKey = input('param.key');
$userId = input('get.uid');
$basicOptions = Option::getValues(['basic']);
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$basicOptions = Option::getValues(['basic','register','login'],$this->userObj->userSQLData);
$this->isLoginStatusCheck();
$resetAction = User::resetUser($resetKey,$userId);
if($resetAction[0]){
return view('reset_user', [
return view('login', [
'options' => $basicOptions,
'RegOptions' => $basicOptions,
'loginStatus' => $this->userObj->loginStatus,
'key' => $resetKey."_".$userId,
'pageId' => "resetPwdForm",
]);
}else{
$this->error($resetAction[1],403,$basicOptions);
@@ -185,7 +201,7 @@ class Member extends Controller{
$policyList[$key] = $value;
}
$avaliablePolicy = Db::name("policy")->where("id","in",$policyList)->select();
$basicOptions = Option::getValues(['basic']);
$basicOptions = Option::getValues(['basic'],$this->userObj->userSQLData);
return view('setting', [
'options' => $basicOptions,
'userInfo' => $userInfo,
@@ -236,6 +252,17 @@ class Member extends Controller{
}
}
public function ChangeThemeColor(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->loginStatusCheck();
$saveAction = $this->userObj->changeOption("preferTheme",input("post.theme"));
if($saveAction[0]){
return json(['error' => '200','msg' => '设置成功']);
}else{
return json(['error' => '1','msg' => $saveAction[1]]);
}
}
public function HomePage(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$userInfo = $this->userObj->getInfo();
+57 -38
View File
@@ -14,11 +14,63 @@ class Profile extends Controller{
public $siteOptions;
public function _initialize(){
$this->siteOptions = Option::getValues(["basic"]);
}
public function getList(){
$userId = (string)input("post.uid");
$userData = Db::name("users")->where("id",$userId)->find();
$page = (int)input("post.page");
if (empty($userId) || empty($userData) || $userData["profile"] == 0){
$this->error('用户主页不存或者用户关闭了个人主页',404,$this->siteOptions);
}
switch (input("post.type")) {
case 'all':
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('share_time DESC')
->page($page.',10')
->select();
break;
case 'hot':
$num = Option::getValue("hot_share_num");
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('download_num DESC')
->limit($num)
->select();
break;
default:
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('share_time DESC')
->page($page.',10')
->select();
break;
}
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["share_pwd"]);
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$pathDir = explode("/",$value["source_name"]);
$listData[$key]["fileData"] = end($pathDir);
}
}
return json($listData);
}
public function index(){
$this->visitorObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic"],$this->visitorObj->userSQLData);
$userId = (string)input("param.uid");
$userData = Db::name("users")->where("id",$userId)->find();
if (empty($userId) || empty($userData) || $userData["profile"] == 0){
@@ -30,47 +82,14 @@ class Profile extends Controller{
->where('type',"public")
->count();
$regDays = (int)((time()-strtotime($userData["user_date"]))/86400);
switch (input("get.type")) {
case 'all':
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->paginate(10);
break;
case 'hot':
$num = Option::getValue("hot_share_num");
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->paginate($num);
break;
default:
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->paginate(10);
break;
}
$listData = $list->all();
foreach ($listData as $key => $value) {
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$pathDir = explode("/",$value["source_name"]);
$listData[$key]["fileData"] = end($pathDir);
}
}
return view("profile",[
"options" => $this->siteOptions,
'loginStatus' => $this->visitorObj->loginStatus,
'userInfo' => $userData,
'userData' => $this->visitorObj->userSQLData,
'targetUserInfo' => $userData,
'userSQL' => $this->visitorObj->userSQLData,
'userInfo' => $this->visitorObj->getInfo(),
'groupData' => $groupData,
'list' => $listData,
'listOrigin' => $list,
'type' => input("get.type"),
'shareCount' => $shareCount,
'regDays' => $regDays,
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\Task;
use \app\index\model\Option;
class Queue extends Controller{
public function __construct(\think\Request $request = null){
$token = Option::getValue("task_queue_token");
if($token==""){
abort(403);
}
if(Request::instance()->header("Authorization") !="Bearer ".$token){
abort(403);
}
}
public function index(){
}
public function basicInfo(){
return json_encode([
"basePath" => ROOT_PATH,
]);
}
public function getList(){
$size = input("get.num");
$tasks = Db::name("task")->where("status","todo")->limit($size)->select();
$taskID = array_column($tasks,"id");
Db::name("task")->where("id","in",$taskID)->update(["status"=>"processing"]);
if(empty($tasks)){
return "none";
}else{
return json_encode($tasks);
}
}
public function getPolicy(){
$id = input("get.id");
$policy = Db::name("policy")->where("id",$id)->find();
if(empty(($policy))){
abort(404);
}else{
return json($policy);
}
}
public function setSuccess(){
$id = input("get.id");
$task = new Task($id);
$task->taskModel = Db::name("task")
->where("id",$id)
//->where("status","processing")
->find();
if(empty($task->taskModel)){
return json(["error"=>true,"msg"=>"未找到任务"]);
}
return json($task->setSuccess());
}
public function setError(){
$id = input("get.id");
$task = new Task($id);
$task->taskModel = Db::name("task")
->where("id",$id)
//->where("status","processing")
->find();
if(empty($task->taskModel)){
return json(["error"=>true,"msg"=>"未找到任务"]);
}
return json($task->Error());
}
}
@@ -0,0 +1,179 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\Aria2;
use think\Session;
class RemoteDownload extends Controller{
public $userObj;
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
if(!$this->userObj->loginStatus){
echo "Bad request";
exit();
}
}
private function checkPerimission($permissionId){
$permissionData = $this->userObj->groupData["aria2"];
if(explode(",",$permissionData)[$permissionId] != "1"){
return false;
}
return true;
}
private function insertRecord($aria2,$url,$path){
Db::name("download")->insert([
"pid" => $aria2->pid,
"path_id" => $aria2->pathId,
"owner" => $this->userObj->uid,
"save_dir" => $path,
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$url,
"file_index" => 0,
"is_single" => 1,
"total_size" => 0,
]);
}
public function addUrl(){
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$this->checkPerimission(0) || ($policyData["policy_type"] != "local" && $policyData["policy_type"] != "onedrive")){
return json(["result"=>['success'=>false,'error'=>"您当前的无用户无法执行此操作"]]);
}
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$downloadStart = $aria2->addUrl(input("post.url"));
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.url"),input("post.path"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
public function AddTorrent(){
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$this->checkPerimission(0) || $policyData["policy_type"] != "local" && $policyData["policy_type"] != "onedrive"){
return json(["result"=>['success'=>false,'error'=>"您当前的无用户无法执行此操作"]]);
}
$downloadingLength = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->sum("total_size");
if(!\app\index\model\FileManage::sotrageCheck($this->userObj->uid,$downloadingLength)){
return json(["result"=>['success'=>false,'error'=>"容量不足"]]);
}
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$torrentObj = new \app\index\model\FileManage(input("post.id"),$this->userObj->uid,true);
$downloadStart = $aria2->addTorrent($torrentObj->signTmpUrl());
if($aria2->reqStatus){
$this->insertRecord($aria2,input("post.id"),input("post.savePath"));
return json(["result"=>['success'=>true,'error'=>null]]);
}else{
return json(["result"=>['success'=>false,'error'=>$aria2->reqMsg]]);
}
}
public function FlushStatus(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
if(!input("?post.id")){
return json(['error'=>1,'message'=>"信息不完整"]);
}
$policyData = Db::name("policy")->where("id",$this->userObj->groupData["policy_name"])->find();
if(!$aria2->flushStatus(input("post.id"),$this->userObj->uid,$policyData)){
return json(['error'=>1,'message'=>$aria2->reqMsg]);
}
}
public function FlushUser(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("owner",$this->userObj->uid)
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
//取消的
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$this->userObj->uid,$this->userObj->getPolicy());
}
}
public function Cancel(){
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$downloadItem = Db::name("download")->where("owner",$this->userObj->uid)->where("id",input("post.id"))->find();
if(empty($downloadItem)){
return json(['error'=>1,'message'=>"未找到下载记录"]);
}
if($aria2->Remove($downloadItem["pid"],"")){
return json(['error'=>0,'message'=>"下载已取消"]);
}else{
return json(['error'=>1,'message'=>"取消失败"]);
}
}
public function ListDownloading(){
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","in",["active","ready","waiting"])->order('id desc')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
public function ListFinished(){
$page = input("get.page");
$downloadItems = Db::name("download")->where("owner",$this->userObj->uid)->where("status","not in",["active","ready","waiting"])->order('id desc')->page($page.',10')->select();
foreach ($downloadItems as $key => $value) {
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$downloadItems[$key]["fileName"] = basename($connectInfo["dir"]);
$downloadItems[$key]["completedLength"] = $connectInfo["completedLength"];
$downloadItems[$key]["totalLength"] = $connectInfo["totalLength"];
$downloadItems[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$downloadItems[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$downloadItems[$key]["fileName"] = $value["source"];
}
$downloadItems[$key]["completedLength"] = 0;
$downloadItems[$key]["totalLength"] = 0;
$downloadItems[$key]["downloadSpeed"] = 0;
}
}
return json($downloadItems);
}
}
+127 -30
View File
@@ -16,7 +16,7 @@ class Share extends Controller{
public function _initialize(){
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
$this->siteOptions = Option::getValues(["basic"]);
$this->siteOptions = Option::getValues(["basic","share"]);
}
public function index(){
@@ -30,34 +30,36 @@ class Share extends Controller{
$shareObj->numIncrease("view_num");
if($shareObj->shareData["source_type"] == "dir"){
return view('share_dir', [
'options' => Option::getValues(['basic','share']),
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'dirData' => $shareObj->dirData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->userSQLData,
'userData' => $this->userObj->getInfo(),
'groupData' => $shareObj->shareOwner->groupData,
'allowPreview' => Option::getValue("allowdVisitorDownload"),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}else{
return view('share_single', [
'options' => Option::getValues(['basic','share']),
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'fileData' => $shareObj->fileData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->userSQLData,
'userData' => $this->userObj->getInfo(),
'allowPreview' => Option::getValue("allowdVisitorDownload"),
'path' => empty(input("get.path"))?"/":input("get.path"),
]);
}
}else{
return view('share_lock', [
'options' => Option::getValues(['basic','share']),
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userInfo' => $shareObj->shareOwner->userSQLData,
'fileData' => $shareObj->fileData,
'shareData' => $shareObj->shareData,
'loginStatus' => $this->userObj->loginStatus,
'userData' => $this->userObj->userSQLData,
'userData' => $this->userObj->getInfo(),
'pwd' => input("?get.pwd") ? input("get.pwd") : "",
]);
}
@@ -66,12 +68,18 @@ class Share extends Controller{
public function getDownloadUrl(){
$shareId = input('key');
$shareObj = new ShareHandler($shareId,false);
return $shareObj->getDownloadUrl($this->userObj);
return json($shareObj->getDownloadUrl($this->userObj));
}
public function Download(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$DownloadHandler = $shareObj->Download($this->userObj);
@@ -85,6 +93,26 @@ class Share extends Controller{
}
}
public function Content(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$contentHandller = $shareObj->getContent($this->userObj,$filePath,false);
}else{
$contentHandller = $shareObj->getContent($this->userObj,$filePath,true);
}
if(!$contentHandller[0]){
return json(["result"=>["success"=>false,"error"=>$contentHandller[1]]]);
}
}
public function chekPwd(){
$shareId = input('key');
$inputPwd = input('password');
@@ -95,12 +123,26 @@ class Share extends Controller{
"msg" => "分享不存在"
);
}
return $shareObj->checkPwd($inputPwd);
return json($shareObj->checkPwd($inputPwd));
}
private function referCheck(){
$agent = Request::instance()->header('referer');
if(substr($agent, 0, strlen($this->siteOptions["siteURL"])) !== $this->siteOptions["siteURL"]){
return false;
}
return true;
}
public function Preview(){
$shareId = input('param.key');
$filePath = input('get.path');
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$previewHandler = $shareObj->Preview($this->userObj);
@@ -122,7 +164,7 @@ class Share extends Controller{
$shareId = input('param.key');
$reqPathTo = stripslashes(json_decode(file_get_contents("php://input"),true)['path']);
$shareObj = new ShareHandler($shareId,false);
return $shareObj->ListFile($reqPathTo);
return json($shareObj->ListFile($reqPathTo));
}
public function ListPic(){
@@ -132,28 +174,95 @@ class Share extends Controller{
return $shareObj->listPic($shareId,$filePath);
}
public function Thumb(){
$shareId = input('param.key');
$filePath = urldecode(input('get.path'));
if(input("get.isImg") != "true"){
return "";
}
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
$Redirect = $shareObj->getThumb($this->userObj,$filePath);
if($Redirect[0]){
$this->redirect($Redirect[1],302);
}else{
$this->error($Redirect[1],403,$this->siteOptions);
}
}
public function DocPreview(){
$shareId = input('param.key');
$filePath = urldecode(input('get.path'));
if($this->siteOptions["refererCheck"]=="true"){
$check = $this->referCheck();
if(!$check){
$this->error("来源非法",403,$this->siteOptions);
}
}
$shareObj = new ShareHandler($shareId,false);
if(empty($filePath)){
$Redirect = $shareObj->getDocPreview($this->userObj,$filePath,false);
}else{
$Redirect = $shareObj->getDocPreview($this->userObj,$filePath,true);
}
if($Redirect[0]){
$this->redirect($Redirect[1],302);
}else{
$this->error($Redirect[1],403,$this->siteOptions);
}
}
public function Delete(){
$shareId = input('post.id');
$shareObj = new ShareHandler($shareId,false);
if(!$shareObj->querryStatus){
return array(
return json(array(
"error" => 1,
"msg" => "分享不存在"
);
));
}
return $shareObj->deleteShare($this->userObj->uid);
return json($shareObj->deleteShare($this->userObj->uid));
}
public function ChangePromission(){
$shareId = input('post.id');
$shareObj = new ShareHandler($shareId,false);
if(!$shareObj->querryStatus){
return array(
return json(array(
"error" => 1,
"msg" => "分享不存在"
);
));
}
return $shareObj->changePromission($this->userObj->uid);
return json($shareObj->changePromission($this->userObj->uid));
}
public function ListMyShare(){
if(!$this->userObj->loginStatus){
$this->redirect(url('/Login','',''));
exit();
}
$list = Db::name('shares')
->where('owner',$this->userObj->uid)
->order('share_time DESC')
->page(input("post.page").",18")
->select();
$listData = $list;
foreach ($listData as $key => $value) {
unset($listData[$key]["source_name"]);
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$listData[$key]["fileData"] = $value["source_name"];
}
}
return json($listData);
}
public function My(){
@@ -163,22 +272,10 @@ class Share extends Controller{
}
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$list = Db::name('shares')->where('owner',$this->userObj->uid)->paginate(10);
$listData = $list->all();
foreach ($listData as $key => $value) {
if($value["source_type"]=="file"){
$listData[$key]["fileData"] = Db::name('files')->where('id',$value["source_name"])->find()["orign_name"];
}else{
$listData[$key]["fileData"] = $value["source_name"];
}
}
return view('share_home', [
'options' => Option::getValues(['basic','share']),
'userInfo' => $userInfo,
'options' => Option::getValues(['basic','share'],$this->userObj->userSQLData),
'userData' => $userInfo,
'groupData' => $groupData,
'list' => $listData,
'listOrigin' => $list
]);
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use think\Session;
use \app\index\model\FileManage;
use \app\index\model\ShareHandler;
class Viewer extends Controller{
public $userObj;
public function _initialize(){
// $this->userObj = new User(cookie('user_id'),cookie('login_key'));
// if(!$this->userObj->loginStatus){
// $this->redirect(url('/Login','',''));
// exit();
// }
$this->userObj = new User(cookie('user_id'),cookie('login_key'));
}
public function Video(){
$path = input("get.path");
$pathSplit = explode("/",urldecode($path));
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$url = "/File/Preview?action=preview&path=".$path;
if(input("get.share")==true){
$url = "/Share/Preview/".input("get.shareKey")."/?path=".$path;
}else if(input("get.single")==true){
$url = "/Share/Preview/".input("get.shareKey");
}
return view('video', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'groupData' => $groupData,
'url' => $url,
'fileName' => end($pathSplit),
'isSharePage' => input("?get.share")?"true":"false",
]);
}
public function Markdown(){
$path = input("get.path");
$pathSplit = explode("/",urldecode($path));
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$url = "/File/Content?action=preview&path=".$path;
if(input("get.share")==true){
$url = "/Share/Content/".input("get.shareKey")."/?path=".$path;
}else if(input("get.single")==true){
$url = "/Share/Content/".input("get.shareKey");
}
return view('markdown', [
'options' => Option::getValues(['basic'],$this->userObj->userSQLData),
'userInfo' => $userInfo,
'groupData' => $groupData,
'url' => $url,
'fileName' => end($pathSplit),
'path' => urldecode($path),
'isSharePage' => input("?get.share")?"true":"false",
]);
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ use think\Db;
use \app\index\model\Option;
use \app\index\model\User;
use \app\index\model\Directory;
use \app\index\model\Object;
use \app\index\model\Objects;
use \app\index\model\DavAuth;
use \app\index\model\BasicCallBack;
@@ -28,7 +28,7 @@ class WebDav extends Controller{
$publicDir = new Directory($this->uid."/");
$server = new DAV\Server($publicDir);
$server->setBaseUri('/WebDav/Api/uid/'.$this->uid);
$server->setBaseUri('/WebDav/Api/uid/'.$this->uid."/");
$lockBackend = new DAV\Locks\Backend\File(ROOT_PATH.'public/locks');
$lockPlugin = new DAV\Locks\Plugin($lockBackend);
$server->addPlugin($lockPlugin);
+160 -6
View File
@@ -5,6 +5,7 @@ use think\Model;
use think\Db;
use \app\index\model\Mail;
use \app\index\model\FileManage;
use \Krizalys\Onedrive\Client;
class AdminHandler extends Model{
@@ -14,11 +15,30 @@ class AdminHandler extends Model{
public $pageNow;
public $pageTotal;
public $dataTotal;
public $dbVerInfo;
public function __construct($options){
$this->siteOptions = $options;
}
public function checkDbVersion(){
$versionInfo = json_decode(@file_get_contents(ROOT_PATH. "application/version.json"),true);
$dbVerNow = Option::getValue("database_version");
if(!isset($versionInfo["db_version"]) || $dbVerNow < (int)$versionInfo["db_version"]){
$this->dbVerInfo = array('now' => $dbVerNow, 'require' => $versionInfo["db_version"]);
return true;
}
return false;
}
public function checkCron(){
$cronRecord = Db::name("corn")->where("name","delete_unseful_chunks")->find();
if($cronRecord["last_excute"]+$cronRecord["interval_s"]+"1800"<time()){
return false;
}
return true;
}
public function getStatics(){
$statics["fileNum"] = Db::name('files')->count();
$statics["privateShareNum"] = Db::name('shares')->where("type","private")->count();
@@ -82,6 +102,37 @@ class AdminHandler extends Model{
return $this->saveOptions($options);
}
public function saveColorSetting($options){
$colorOptions = [];
foreach ($options["color"] as $key => $value) {
$color = \json_decode($value,true);
$validate = ($color!==null)&&array_key_exists("palette",$color) &&
array_key_exists("common",$color["palette"]) &&
array_key_exists("black",$color["palette"]["common"]) &&
array_key_exists("white",$color["palette"]["common"]) &&
array_key_exists("background",$color["palette"]) &&
array_key_exists("primary",$color["palette"]) &&
array_key_exists("secondary",$color["palette"]) &&
array_key_exists("error",$color["palette"]) &&
array_key_exists("text",$color["palette"]) &&
array_key_exists("explorer",$color["palette"]) &&
array_key_exists("main",$color["palette"]["primary"]) &&
array_key_exists("main",$color["palette"]["secondary"]);
if(!$validate){
return ["error"=>1,"msg"=>"".($key+1)." 行配色方案格式有误"];
}
$colorOptions[$color["palette"]["primary"]["main"]] = $color;
}
if(!array_key_exists($options["header"],$colorOptions)){
return ["error"=>1,"msg"=>"".($key+1)." 默认配色不存在"];
}
$optionsForSet = [
"defaultTheme" => $options["header"],
"themes" => json_encode($colorOptions),
];
return $this->saveOptions($optionsForSet);
}
public function saveRegSetting($options){
foreach(["email_active","login_captcha","reg_captcha","forget_captcha"] as $key){
$options[$key] = array_key_exists($key,$options) ? $options[$key] : 0;
@@ -93,6 +144,14 @@ class AdminHandler extends Model{
return $this->saveOptions($options);
}
public function saveAria2Setting($options){
return $this->saveOptions($options);
}
public function saveTaskOption($options){
return $this->saveOptions($options);
}
public function saveMailTemplate($options){
return $this->saveOptions($options);
}
@@ -102,6 +161,7 @@ class AdminHandler extends Model{
unset($options["sizeTimes"]);
$options["grade_policy"] = 0;
$options["policy_list"] = $options["policy_name"];
$options["aria2"] = $options["aria2"] ? "1,1,1" : "0,0,0";
try {
Db::name("groups")->insert($options);
} catch (Exception $e) {
@@ -130,7 +190,7 @@ class AdminHandler extends Model{
} catch (Exception $e) {
return ["error"=>1,"msg"=>$e->getMessage()];
}
return ["error"=>200,"msg"=>"设置已保存"];
return ["error"=>200,"msg"=>"设置已保存","id"=>Db::name('policy')->getLastInsID()];
}
public function editPolicy($options){
@@ -156,6 +216,7 @@ class AdminHandler extends Model{
unset($options["id"]);
$options["max_storage"] = $options["max_storage"]*$options["sizeTimes"];
unset($options["sizeTimes"]);
$options["aria2"] = $options["aria2"] ? "1,1,1" : "0,0,0";
try {
Db::name("groups")->where("id",$groupId)->update($options);
} catch (Exception $e) {
@@ -336,8 +397,55 @@ class AdminHandler extends Model{
return $userData;
}
public function listDownloads(){
$pageSize = 10;
$this->pageData = Db::name("download")
->order("id desc")
->paginate($pageSize);
$this->dataTotal = Db::name("download")
->order("id desc")
->count();
$this->pageTotal = ceil($this->dataTotal/$pageSize);
$this->listData = $this->pageData->all();
$userCache=[];
$userCacheList=[];
foreach ($this->listData as $key => $value) {
if(in_array($value["owner"], $userCacheList)){
$this->listData[$key]["user"] = $userCache[$value["owner"]];
}else{
$this->listData[$key]["user"] = Db::name("users")->where("id",$value["owner"])->find();
array_push($userCacheList,$value["owner"]);
$userCache[$value["owner"]] = $this->listData[$key]["user"];
}
$connectInfo = json_decode($value["info"],true);
if(isset($connectInfo["dir"])){
$this->listData[$key]["fileName"] = basename($connectInfo["dir"]);
$this->listData[$key]["completedLength"] = $connectInfo["completedLength"];
$this->listData[$key]["totalLength"] = $connectInfo["totalLength"];
$this->listData[$key]["downloadSpeed"] = $connectInfo["downloadSpeed"];
}else{
if(floor($value["source"])==$value["source"]){
$this->listData[$key]["fileName"] = Db::name("files")->where("id",$value["source"])->column("orign_name");
}else{
$this->listData[$key]["fileName"] = $value["source"];
}
$this->listData[$key]["completedLength"] = 0;
$this->listData[$key]["totalLength"] = 0;
$this->listData[$key]["downloadSpeed"] = 0;
}
}
$this->pageNow = input("?get.page")?input("get.page"):1;
}
public function getTasks(){
$taskData = Db::name("task")
->order("id DESC")
->paginate(10);
return $taskData;
}
public function listFile(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodFile')) ? "id DESC" : cookie('orderMethodFile');
$this->pageData = Db::name("files")
->where(function ($query) {
@@ -392,7 +500,7 @@ class AdminHandler extends Model{
}
public function listUser(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodUser')) ? "id DESC" : cookie('orderMethodUser');
$this->pageData = Db::name("users")
->where(function ($query) {
@@ -461,8 +569,8 @@ class AdminHandler extends Model{
}
public function listShare(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodShare')) ? "id DESC" : cookie('orderMethodShare');
$pageSize = !cookie('?pageSize') ? 10 : cookie('pageSize');
$orderType = empty(cookie('orderMethodShare')) ? "share_time DESC" : cookie('orderMethodShare');
$this->pageData = Db::name("shares")
->where(function ($query) {
if(!empty(cookie('shareSearch'))){
@@ -506,7 +614,7 @@ class AdminHandler extends Model{
}
public function listPolicy(){
$pageSize = empty(cookie('pageSize')) ? 10 : cookie('pageSize');
$pageSize =!cookie('?pageSize') ? 10 : cookie('pageSize');
$this->pageData = Db::name("policy")
->where(function ($query) {
if(!empty(cookie('policySearch'))){
@@ -652,6 +760,52 @@ class AdminHandler extends Model{
}
return ["error"=>0,"msg"=>"设置已保存"];
}
public function updateOnedriveToken($policyId){
$policyData = Db::name("policy")->where("id",$policyId)->find();
if(empty($policyData)){
throw new \think\Exception("Policy not found");
}
$onedrive = new Client([
'client_id' => $policyData["bucketname"],
]);
$url = $onedrive->getLogInUrl([
'offline_access',
'files.readwrite.all',
], Option::getValue("siteURL")."Admin/oneDriveCalllback");
echo "<script>location.href='".$url."'</script>正在跳转至Onedrive账号授权页面,如果没有跳转,请<a href='$url'>点击这里</a>。";
Db::name("policy")->where("id",$policyId)->update([
"sk" => json_encode($onedrive->getState()),
]);
\think\Session::set('onedrive.pid',$policyId);
}
public function oneDriveCalllback($code){
if(input("?get.error")){
throw new \think\Exception(input("get.error_description"));
}
$policyId = \think\Session::get('onedrive.pid');
$policyData = Db::name("policy")->where("id",$policyId)->find();
$onedrive = new Client([
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
// Obtain the token using the code received by the OneDrive API.
$onedrive->obtainAccessToken($policyData["ak"], $_GET['code']);
// Persist the OneDrive client' state for next API requests.
Db::name("policy")->where("id",$policyId)->update([
"sk" => json_encode($onedrive->getState()),
]);
echo "<script>location.href='/Admin/PolicyList?page=1'</script>";
}
}
?>
+469
View File
@@ -0,0 +1,469 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
class Aria2 extends Model{
private $authToken;
private $apiUrl;
private $savePath;
private $saveOptions;
public $reqStatus;
public $reqMsg;
public $pathId;
public $pid;
private $uid;
private $policy;
public function __construct($options){
$this->authToken = $options["aria2_token"];
$this->apiUrl = rtrim($options["aria2_rpcurl"],"/")."/";
$this->saveOptions = json_decode($options["aria2_options"],true);
$this->savePath = rtrim(rtrim($options["aria2_tmppath"],"/"),"\\").DS;
}
/**
* 新建普通URL下载任务
*
* @param string $url
* @return void
*/
public function addUrl($url){
$this->pathId = uniqid();
$reqFileds = [
"params" => ["token:".$this->authToken,
[$url],["dir" => $this->savePath.$this->pathId],
],
"jsonrpc" => "2.0",
"id" => $this->pathId,
"method" => "aria2.addUri"
];
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
/**
* 新建种子下载任务
*
* @param string $torrentUrl 种子URL
* @return void
*/
public function addTorrent($torrentUrl){
$this->pathId = uniqid();
$reqFileds = [
"params" => ["token:".$this->authToken,
[$torrentUrl],["dir" => $this->savePath.$this->pathId],
],
"jsonrpc" => "2.0",
"id" => $this->pathId,
"method" => "aria2.addUri"
];
$reqFileds["params"][2] = array_merge($reqFileds["params"][2],$this->saveOptions);
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
$this->reqStatus = 1;
$this->pid = $respondData["result"];
}else{
$this->reqStatus = 0;
$this->reqMsg = isset($respondData["error"]["message"]) ? $respondData["error"]["message"] : $this->reqMsg;
}
}
/**
* 刷新下载状态
*
* @param int $id 任务ID
* @param int $uid 用户ID
* @param array $policy 上传策略
* @return void
*/
public function flushStatus($id,$uid,$policy){
$this->uid = $uid;
if(empty($policy)){
$user = Db::name("users")->where("id",$uid)->find();
$group = Db::name("groups")->where("id",$user["user_group"])->find();
$policy = Db::name("policy")->where("id",$group["policy_name"])->find();
}
$this->policy = $policy;
$downloadInfo = Db::name("download")->where("id",$id)->find();
if(empty($downloadInfo)){
$this->reqStatus = 0;
$this->reqMsg = "未找到下载记录";
return false;
}
if(in_array($downloadInfo["status"], ["error","complete"])){
$this->reqStatus = 1;
return true;
}
if($uid != $downloadInfo["owner"]){
$this->reqStatus = 0;
$this->reqMsg = "无权操作";
return false;
}
$reqFileds = [
"params" => ["token:".$this->authToken,$downloadInfo["pid"]],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.tellStatus"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
if($this->storageCheck($respondData["result"],$downloadInfo)){
if($downloadInfo["is_single"] && count($respondData["result"]["files"]) >1){
$this->updateToMuiltpe($respondData["result"],$downloadInfo);
return false;
}
if(isset($respondData["result"]["followedBy"])){
Db::name("download")->where("id",$id)
->update([
"pid" => $respondData["result"]["followedBy"][0],
]);
return false;
}
Db::name("download")->where("id",$id)
->update([
"status" => $respondData["result"]["status"],
"last_update" => date("Y-m-d h:i:s"),
"info" => json_encode([
"completedLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"],
"totalLength" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
"dir" => $respondData["result"]["files"][$downloadInfo["file_index"]]["path"],
"downloadSpeed" => $respondData["result"]["downloadSpeed"],
"errorMessage" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
]),
"msg" => isset($respondData["result"]["errorMessage"]) ? $respondData["result"]["errorMessage"] : "",
"total_size" => $respondData["result"]["files"][$downloadInfo["file_index"]]["length"],
]);
switch ($respondData["result"]["status"]) {
case 'complete':
$this->setComplete($respondData["result"],$downloadInfo);
break;
case 'removed':
$this->setCanceled($respondData["result"],$downloadInfo);
break;
default:
# code...
break;
}
if(($respondData["result"]["files"][$downloadInfo["file_index"]]["completedLength"] == $respondData["result"]["files"][$downloadInfo["file_index"]]["length"] && ($respondData["result"]["files"][$downloadInfo["file_index"]]["length"] !=0 )) && $respondData["result"]["status"]=="active"){
$this->setComplete($respondData["result"],$downloadInfo,$downloadInfo["file_index"]);
Db::name("download")->where("id",$id)
->update([
"status" => "complete",
]);
}
}else{
$this->reqStatus = 0;
$this->reqMsg = "空间容量不足";
$this->setError($respondData["result"],$downloadInfo,"空间容量不足");
return false;
}
}else{
$this->reqStatus = 0;
$this->reqMsg = $respondData["error"]["message"];
$this->setError($respondData,$downloadInfo,$respondData["error"]["message"],"error",true);
return false;
}
return true;
}
/**
* 取消任务
*
* @param array $quenInfo 任务信息(aria2
* @param array $sqlData 任务信息(数据库)
* @return void
*/
private function setCanceled($quenInfo,$sqlData){
@self::remove_directory($this->savePath.$sqlData["path_id"]);
if(!is_dir($this->savePath.$sqlData["path_id"])){
Db::name("download")->where("id",$sqlData["id"])->update([
"status" => "canceled",
]);
}
}
/**
* 移除整个目录
*
* @param string $dir
* @return void
*/
static function remove_directory($dir){
if($handle=opendir("$dir")){
while(false!==($item=readdir($handle))){
if($item!="."&&$item!=".."){
if(is_dir("$dir/$item")){
self::remove_directory("$dir/$item");
}else{
unlink("$dir/$item");
}
}
}
closedir($handle);
rmdir($dir);
}
}
/**
* 将单文件任务升级至多文件任务
*
* @param array $quenInfo
* @param array $sqlData
* @return void
*/
private function updateToMuiltpe($quenInfo,$sqlData){
foreach ($quenInfo["files"] as $key => $value) {
Db::name("download")->insert([
"pid" => $sqlData["pid"],
"path_id" => $sqlData["path_id"],
"owner" => $sqlData["owner"],
"save_dir" => $sqlData["save_dir"],
"status" => "ready",
"msg" => "",
"info"=>"",
"source" =>$sqlData["source"],
"file_index" => $key,
"is_single" => 0,
"total_size" => 0,
]);
}
Db::name("download")->where("id",$sqlData["id"])->delete();
}
/**
* 下载完成后续处理
*
* @param array $quenInfo
* @param array $sqlData
* @param int $fileIndex
* @return void
*/
private function setComplete($quenInfo,$sqlData,$fileIndex=null){
if($this->policy["policy_type"] != "local" && $this->policy["policy_type"] != "onedrive"){
$this->setError($quenInfo,$sqlData,"您当前的上传策略无法使用离线下载");
return false;
}
if($fileIndex==null){
$this->forceRemove($sqlData["pid"]);
}
$suffixTmp = explode('.', $quenInfo["dir"]);
$fileSuffix = array_pop($suffixTmp);
$uploadHandller = new UploadHandler($this->policy["id"],$this->uid);
$allowedSuffix = explode(',', $uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)));
$sufficCheck = !in_array($fileSuffix,$allowedSuffix);
if(empty($uploadHandller->getAllowedExt(json_decode($this->policy["filetype"],true)))){
$sufficCheck = false;
}
if($sufficCheck){
//取消任务
$this->setError($quenInfo,$sqlData,"文件类型不被允许");
return false;
}
if($this->policy['autoname']){
$fileName = $uploadHandller->getObjName($this->policy['namerule'],"local",basename($quenInfo["files"][$sqlData["file_index"]]["path"]));
}else{
$fileName = basename($quenInfo["files"][$sqlData["file_index"]]["path"]);
}
$generatePath = $uploadHandller->getDirName($this->policy['dirrule']);
if($this->policy["policy_type"] == "onedrive"){
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
$task = new Task();
$task->taskName = "Upload RemoteDownload File " . $quenInfo["files"][$sqlData["file_index"]]["path"] . " to Onedrive";
$task->taskType = $quenInfo["files"][$sqlData["file_index"]]["length"]<=4*1024*1024 ? "UploadRegularRemoteDownloadFileToOnedrive" :"UploadLargeRemoteDownloadFileToOnedrive";
@list($width, $height, $type, $attr) = getimagesize($quenInfo["files"][$sqlData["file_index"]]["path"]);
$picInfo = empty($width)?"":$width.",".$height;
$task->taskContent = json_encode([
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
"originPath" => $quenInfo["files"][$sqlData["file_index"]]["path"],
"objname" => $fileName,
"savePath" => $generatePath,
"fsize" => (int)$quenInfo["files"][$sqlData["file_index"]]["length"],
"picInfo" => $picInfo,
"policyId" => $this->policy["id"],
]);
$task->userId = $this->uid;
$task->saveTask();
}else{
$savePath = ROOT_PATH . 'public/uploads/'.$generatePath.DS.$fileName;
is_dir(dirname($savePath))? :mkdir(dirname($savePath),0777,true);
rename($quenInfo["files"][$sqlData["file_index"]]["path"],$savePath);
@unlink(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
$jsonData = array(
"path" => ltrim(str_replace("/", ",", $sqlData["save_dir"]),","),
"fname" => basename($quenInfo["files"][$sqlData["file_index"]]["path"]),
"objname" => $generatePath.DS.$fileName,
"fsize" => $quenInfo["files"][$sqlData["file_index"]]["length"],
);
@list($width, $height, $type, $attr) = getimagesize($savePath);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$this->policy,$this->uid,$picInfo);
if(!$addAction[0]){
//取消任务
$this->setError($quenInfo,$sqlData,$addAction[1]);
return false;
}
}
FileManage::storageCheckOut($this->uid,$quenInfo["files"][$sqlData["file_index"]]["length"]);
}
/**
* 设置任务为失败状态
*
* @param array $quenInfo
* @param array $sqlData
* @param string $msg 失败消息
* @param string $status 状态
* @param boolean $delete 是否删除下载文件
* @return void
*/
private function setError($quenInfo,$sqlData,$msg,$status="error",$delete=true){
$this->Remove($sqlData["pid"],$sqlData);
$this->removeDownloadResult($sqlData["pid"],$sqlData);
if($delete){
if(isset($quenInfo["files"][$sqlData["file_index"]]["path"]) && file_exists($quenInfo["files"][$sqlData["file_index"]]["path"])){
$deleteAction = @unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
if(file_exists(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]))){
$task = new Task();
$task->taskName = "Delete remote download temp file";
$task->taskType = "deleteFolder";
$task->taskContent = json_encode([
"folder" => dirname($quenInfo["files"][$sqlData["file_index"]]["path"]),
]);
$task->userId = $this->uid;
$task->saveTask();
}
}
}
Db::name("download")->where("id",$sqlData["id"])->update([
"msg" => $msg,
"status" => $status,
]);
}
/**
* 移除任务
*
* @param int $gid
* @param array $sqlData
* @return void
*/
public function Remove($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.remove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 删除下载结果
*
* @param int $gid
* @param array $sqlData
* @return void
*/
public function removeDownloadResult($gid,$sqlData){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.removeDownloadResult"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 强制移除任务
*
* @param int $gid
* @return void
*/
public function forceRemove($gid){
$reqFileds = [
"params" => ["token:".$this->authToken,$gid],
"jsonrpc" => "2.0",
"id" => uniqid(),
"method" => "aria2.forceRemove"
];
$reqFileds = json_encode($reqFileds,JSON_OBJECT_AS_ARRAY);
$respondData = $this->sendReq($reqFileds);
if(isset($respondData["result"])){
return true;
}
return false;
}
/**
* 检查容量
*
* @param array $quenInfo
* @param array $sqlData
* @return void
*/
private function storageCheck($quenInfo,$sqlData){
if(!FileManage::sotrageCheck($this->uid,$quenInfo["totalLength"])){
return false;
}
if(!FileManage::sotrageCheck($this->uid,$quenInfo["completedLength"])){
return false;
}
return true;
}
/**
* 发送请求
*
* @param string $data
* @return array
*/
private function sendReq($data){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $this->apiUrl."jsonrpc");
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$tmpInfo = curl_exec($curl);
if (curl_errno($curl)) {
$this->reqStatus = 0;
$this->reqMsg = "请求失败,".curl_error($curl);
}
curl_close($curl);
return json_decode($tmpInfo,true);
}
}
?>
+4 -4
View File
@@ -4,7 +4,7 @@ namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\FileManage;
use \app\index\model\LocalAdapter;
class Avatar extends Model{
@@ -20,7 +20,7 @@ class Avatar extends Model{
if(!$new){
$userData = Db::name("users")->where('id',$obj)->find();
$this->userData = $userData;
if($userData["avatar"] == "default"){
if(empty($userData)||$userData["avatar"] == "default"){
$this->avatarType = "default";
}else{
$avatarPrarm = explode(".",$userData["avatar"]);
@@ -90,7 +90,7 @@ class Avatar extends Model{
$filePath = ROOT_PATH . 'public/avatars/' . $this->fileName.$siezSuffix;
if(file_exists($filePath)){
ob_end_clean();
header('Content-Type: '.FileManage::getMimetype($filePath));
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
@@ -131,7 +131,7 @@ class Avatar extends Model{
}
ob_end_clean();
$filePath = ROOT_PATH . 'static/img/default.png' .$siezSuffix;
header('Content-Type: '.FileManage::getMimetype($filePath));
header('Content-Type: '.LocalAdapter::getMimetype($filePath));
$fileObj = fopen($filePath,"r");
while(!feof($fileObj)){
echo fread($fileObj,2097152);
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
class B2 extends Model{
public $policy;
private $ak;
public function __construct($policy){
$this->policy = $policy;
$akInfo = explode(":",$this->policy["ak"]);
$this->ak = $akInfo[0];
if(time()-$akInfo[1]>=86000){
$this->updateAuth();
}
}
private function updateAuth(){
$credentials = base64_encode($this->ak . ":" . $this->policy["sk"]);
$url = "https://api.backblazeb2.com/b2api/v1/b2_authorize_account";
$session = curl_init($url);
$headers = array();
$headers[] = "Accept: application/json";
$headers[] = "Authorization: Basic " . $credentials;
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_HTTPGET, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
$server_output = curl_exec($session);
curl_close ($session);
$authInfo = json_decode($server_output,true);
Db::name("policy")->where("id",$this->policy["id"])
->update([
"ak" => $this->ak . ":" . time(),
"op_name" => $authInfo["apiUrl"],
"url" => $authInfo["downloadUrl"],
"op_pwd" => $authInfo["authorizationToken"],
]);
$this->policy = Db::name("policy")->where("id",$this->policy["id"])->find();
$this->updateUploadAuth();
}
private function updateUploadAuth(){
$session = curl_init($this->policy["op_name"] . "/b2api/v1/b2_get_upload_url");
$data = array("bucketId" => $this->policy["bucketname"]);
$post_fields = json_encode($data);
curl_setopt($session, CURLOPT_POSTFIELDS, $post_fields);
$headers = array();
$headers[] = "Authorization: " . $this->policy["op_pwd"];
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false); //不验证证书
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false); //不验证证书
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($session);
curl_close ($session);
$authInfo = json_decode($server_output,true);
Db::name("policy")->where("id",$this->policy["id"])
->update([
"server" => $authInfo["uploadUrl"] . "|" . $authInfo["authorizationToken"],
]);
$this->policy = Db::name("policy")->where("id",$this->policy["id"])->find();
die("");
}
}
?>
+45 -5
View File
@@ -20,6 +20,28 @@ class CallbackHandler extends Model{
$this->CallbackData = $data;
}
public function remoteHandler($header){
$jsonData = json_decode(base64_decode($this->CallbackData),true);
$CallbackSqlData = Db::name('callback')->where('callback_key',$jsonData['callbackkey'])->find();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
if(!$this->IsRemoteCallback($header)){
$this->setError("Undelegated Request");
}
if($this->policyData == null){
$this->setError("CallbackKey Not Exist.");
}
if(!FileManage::sotrageCheck($CallbackSqlData["uid"],$jsonData["fsize"])){
$this->setError("空间容量不足",true);
}
$picInfo = $jsonData["picinfo"];
$addAction = FileManage::addFile($jsonData,$this->policyData,$CallbackSqlData["uid"],$picInfo);
if(!$addAction[0]){
$this->setError($addAction[1],true);
}
FileManage::storageCheckOut($CallbackSqlData["uid"],$jsonData["fsize"]);
$this->setSuccess($jsonData['fname']);
}
public function qiniuHandler($header){
$jsonData = json_decode($this->CallbackData,true);
$CallbackSqlData = Db::name('callback')->where('callback_key',$jsonData['callbackkey'])->find();
@@ -35,6 +57,7 @@ class CallbackHandler extends Model{
$this->setError("空间容量不足",true);
}
$picInfo = $jsonData["picinfo"];
$picInfo = $picInfo==="null,null"?"":$picInfo;
$addAction = FileManage::addFile($jsonData,$this->policyData,$CallbackSqlData["uid"],$picInfo);
if(!$addAction[0]){
$this->setError($addAction[1],true);
@@ -98,6 +121,7 @@ class CallbackHandler extends Model{
if(empty($CallbackSqlData)){
$this->setError("Undelegated Request",false,true);
}
Db::name('callback')->where('callback_key',$key)->delete();
$this->policyData = Db::name('policy')->where('id',$CallbackSqlData['pid'])->find();
$this->userData = Db::name('users')->where('id',$CallbackSqlData['uid'])->find();
$paths = explode("/",$this->CallbackData["key"]);
@@ -113,7 +137,7 @@ class CallbackHandler extends Model{
if(!$jsonData["fsize"]){
$this->setError("File not exist",false,true);
}
$jsonData["fsize"] = $jsonData["fsize"]["size"];
$jsonData["fsize"] = $jsonData["fsize"];
$picInfo = "";
$addAction = FileManage::addFile($jsonData,$this->policyData,$this->userData["id"],"");
if(!$addAction[0]){
@@ -125,14 +149,25 @@ class CallbackHandler extends Model{
}
private function getS3FileInfo(){
$s3 = new \S3\S3($this->policyData["ak"], $this->policyData["sk"],false,$this->policyData["op_pwd"]);
$s3->setSignatureVersion('v4');
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyData["op_name"],
'endpoint' => $this->policyData["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyData["ak"],
'secret' => $this->policyData["sk"],
],
]);
try {
$returnVal = $s3->getObjectInfo($this->policyData["bucketname"],$this->CallbackData["key"]);
$returnVal = $s3->headObject([
'Bucket'=>$this->policyData["bucketname"],
'Key'=>$this->CallbackData["key"]
]);
} catch (Exception $e) {
return false;
}
return $returnVal;
return $returnVal["ContentLength"];
}
public function setSuccess($fname){
@@ -172,6 +207,11 @@ class CallbackHandler extends Model{
}
}
private function IsRemoteCallback($header){
$signKey = hash_hmac("sha256",$this->CallbackData,$this->policyData["sk"]);
return ($signKey == $header);
}
public function IsOssCallback($auth,$pubKey){
if (empty($auth) || empty($pubKey)){
header("http/1.1 403 Forbidden");
+104
View File
@@ -7,6 +7,8 @@ use \think\Session;
use \app\index\model\FileManage;
use \app\index\model\Option;
use \app\index\model\Mail;
use \app\index\model\Aria2;
use think\Exception;
class CronHandler extends Model{
@@ -40,6 +42,21 @@ class CronHandler extends Model{
$this->deleteCallbackData($value["interval_s"]);
}
break;
case 'flush_aria2':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->flushAria2($value["interval_s"]);
}
break;
case 'flush_onedrive_token':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->flushOnedriveToken($value["interval_s"]);
}
break;
case 'delete_remote_downloader_failed_folder':
if($this->checkInterval($value["interval_s"],$value["last_excute"])){
$this->deleteRemoteDownloaderFailedFolder($value["interval_s"]);
}
break;
default:
# code...
break;
@@ -69,5 +86,92 @@ class CronHandler extends Model{
$this->setComplete("delete_callback_data");
}
public function flushAria2($interval){
echo("flushingAria2Status...");
$aria2Options = Option::getValues(["aria2"]);
$aria2 = new Aria2($aria2Options);
$toBeFlushed = Db::name("download")
->where("status","<>","complete")
->where("status","<>","error")
->where("status","<>","canceled")
->select();
foreach ($toBeFlushed as $key => $value) {
$aria2->flushStatus($value["id"],$value["owner"],null);
}
echo("Complete<br>");
$this->setComplete("flush_aria2");
}
public function deleteRemoteDownloaderFailedFolder($interval){
echo("flushOnedriveToken...");
$toBeDeleted = Db::name("task")
->where("type","deleteFolder")
->where("status","todo")
->select();
$success=[];
$todo = [];
foreach ($toBeDeleted as $key => $value) {
$attr = json_decode($value["attr"],true);
if(file_exists($attr["folder"])){
self::remove_directory($attr["folder"]);
}
if(file_exists($attr["folder"])){
$todo[] = $value["id"];
}else{
$success[] = $value["id"];
}
}
Db::name("task")->where("id","in",$success)->update(["status"=>"success"]);
}
/**
* 移除整个目录
*
* @param string $dir
* @return void
*/
static function remove_directory($dir){
if($handle=opendir("$dir")){
while(false!==($item=readdir($handle))){
if($item!="."&&$item!=".."){
if(is_dir("$dir/$item")){
self::remove_directory("$dir/$item");
}else{
unlink("$dir/$item");
}
}
}
closedir($handle);
rmdir($dir);
}
}
public function flushOnedriveToken($interval){
echo("flushOnedriveToken...");
$toBeFlushedPolicy = Db::name("policy")->where("policy_type","onedrive")->select();
foreach ($toBeFlushedPolicy as $key => $value) {
$onedrive = new \Krizalys\Onedrive\Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $value["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($value["sk"]),
]);
try{
$onedrive->renewAccessToken($value["ak"]);
}catch(\Exception $e){
}
Db::name("policy")->where("id",$value["id"])->update([
"sk" => json_encode($onedrive->getState()),
]);
}
echo("Complete<br>");
$this->setComplete("flush_onedrive_token");
}
}
?>
+11 -5
View File
@@ -30,6 +30,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
function createFile($name, $data = NULL){
$name = str_replace(" ","",$name);
$userData = Db::name("users")->where("id",$this->uid)->find();
$groupData = Db::name("groups")->where("id",$userData["user_group"])->find();
$policyData = Db::name("policy")->where("id",$groupData["policy_name"])->find();
@@ -47,7 +48,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
$fileSize = fstat($data)["size"];
if(empty($fileSize)){
$fileSize = 0;
$fileSize = -1;
}
if($fileSize>$policyData["max_size"]){
throw new DAV\Exception\InsufficientStorage('File is to large');
@@ -66,6 +67,9 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
mkdir($savePath,0777,true);
}
file_put_contents($savePath."/".$fileName, $data);
if($fileSize<=0){
$fileSize = filesize($savePath."/".$fileName);
}
$jsonData = array(
"path" => str_replace("/",",",ltrim($this->myPath,"/")),
"fname" => $name,
@@ -120,7 +124,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
function getChildFile($name){
$path = $this->uid.rtrim($this->myPath,"/") . '/' . $name["orign_name"];
return new Object($path);
return new Objects($path);
}
function getChildDir($name){
@@ -134,6 +138,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
}
function getChild($name) {
$name = str_replace(" ","",$name);
if(!$this->childExists($name)){
throw new DAV\Exception\NotFound('File with name ' . $name . ' could not be located');
}
@@ -142,12 +147,13 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
$returnObj = new Directory($path);
return $returnObj;
}else{
return new Object($path);
return new Objects($path);
}
}
function childExists($name) {
$fileObj = new Object($this->uid.rtrim($this->myPath,"/") . '/' . $name);
$name = str_replace(" ","",$name);
$fileObj = new Objects($this->uid.rtrim($this->myPath,"/") . '/' . $name);
if($this->findDir(rtrim($this->myPath,"/") . '/' . $name) || $fileObj->isExist){
return true;
}
@@ -162,7 +168,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota{
$dirName = end($explode);
$rootPath = rtrim($path,"/".$dirName);
$rootPath = empty($rootPath) ? "/" : $rootPath;
$dirData = Db::name('folders')->where('owner',$this->uid)->where('position',dirname($path) == "\\" ?"/":dirname($path))->where("folder_name",basename($path))->find();
$dirData = Db::name('folders')->where('owner',$this->uid)->where('position',dirname($path) == "\\" ?"/":dirname($path))->where("folder_name",getDirName($path))->find();
if(empty($dirData)){
return false;
}
File diff suppressed because it is too large Load Diff
+392
View File
@@ -0,0 +1,392 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
/**
* 本地策略文件管理适配器
*/
class LocalAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 内容
*/
public function getFileContent(){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
$fileObj = fopen($filePath,"r");
$fileContent = fread($fileObj,filesize($filePath)+1);
return $fileContent;
}
/**
* 保存可编辑文件
*
* @param string $content 要保存的文件内容
* @return void
*/
public function saveContent($content){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
file_put_contents($filePath, "");
file_put_contents($filePath, $content);
}
/**
* 输出预览文件
*
* @param boolean $isAdmin 是否为管理员请求
* @return mixed 文件数据
*/
public function Preview($isAdmin = false){
$speedLimit = Db::name('groups')->where('id',$this->userModel["user_group"])->find();
$rangeTransfer = $speedLimit["range_transfer"];
$speedLimit = $speedLimit["speed"];
$sendFileOptions = Option::getValues(["download"]);
if($sendFileOptions["sendfile"] == "1" && !empty($sendFileOptions)){
$this->sendFile($speedLimit,$rangeTransfer,false,$sendFileOptions["header"]);
}else{
if($isAdmin){
$speedLimit="";
}
if($speedLimit == "0"){
exit();
}else if(empty($speedLimit)){
header("Cache-Control: max-age=10800");
$this->outputWithoutLimit(false,$rangeTransfer);
exit();
}else if((int)$speedLimit > 0){
header("Cache-Control: max-age=10800");
$this->outputWithLimit($speedLimit);
}
}
}
/**
* 使用Sendfile模式发送文件数据
*
* @param int $speed 下载限速
* @param boolean $range 是否支持断点续传
* @param boolean $download 是否为下载请求
* @param string $header Sendfile Header
* @return void
*/
private function sendFile($speed,$range,$download=false,$header="X-Sendfile"){
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
$realPath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
if($header == "X-Accel-Redirect"){
$filePath = '/public/uploads/' . $this->fileModel["pre_name"];
}
if($download){
$filePath = str_replace("\\","/",$filePath);
if($header == "X-Accel-Redirect"){
ob_flush();
flush();
echo "s";
}
//保证如下顺序,否则最终浏览器中得到的content-type为'text/html'
//1,写入 X-Sendfile 头信息
$pathToFile = str_replace('%2F', '/', rawurlencode($filePath));
header($header.": ".$pathToFile);
//2,写入Content-Type头信息
$mime_type = self::getMimetypeOnly($realPath);
header('Content-Type: '.$mime_type);
//3,写入正确的附件文件名头信息
$orign_fname = $this->fileModel["orign_name"];
$ua = $_SERVER["HTTP_USER_AGENT"]; // 处理不同浏览器的兼容性
if (preg_match("/Firefox/", $ua)) {
$encoded_filename = rawurlencode($orign_fname);
header("Content-Disposition: attachment; filename*=\"utf8''" . $encoded_filename . '"');
} else if (preg_match("/MSIE/", $ua) || preg_match("/Edge/", $ua) || preg_match("/rv:/", $ua)) {
$encoded_filename = rawurlencode($orign_fname);
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
} else {
// for Chrome,Safari etc.
header('Content-Disposition: attachment;filename="'. $orign_fname .'";filename*=utf-8'."''". $orign_fname);
}
exit;
}else{
$filePath = str_replace("\\","/",$filePath);
header('Content-Type: '.self::getMimetype($realPath));
if($header == "X-Accel-Redirect"){
ob_flush();
flush();
echo "s";
}
header($header.": ".str_replace('%2F', '/', rawurlencode($filePath)));
ob_flush();
flush();
}
}
/**
* 无限速发送文件数据
*
* @param boolean $download 是否为下载
* @param boolean $reload 是否支持断点续传
* @return void
*/
public function outputWithoutLimit($download = false,$reload = false){
ignore_user_abort(false);
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
set_time_limit(0);
session_write_close();
$file_size = filesize($filePath);
$ranges = $this->getRange($file_size);
if($reload == 1 && $ranges!=null){
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges:bytes');
header(sprintf('content-length:%u',$ranges['end']-$ranges['start']));
header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end']-1, $file_size));
}
if($download){
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($filePath));
$encoded_fname = rawurlencode($this->fileModel["orign_name"]);
header('Content-Disposition: attachment;filename="'.$encoded_fname.'";filename*=utf-8'."''".$encoded_fname);
ob_flush();
flush();
}
if(file_exists($filePath)){
if(!$download){
header('Content-Type: '.self::getMimetype($filePath));
ob_flush();
flush();
}
$fileObj = fopen($filePath,"rb");
if($reload == 1){
fseek($fileObj, sprintf('%u', $ranges['start']));
}
while(!feof($fileObj)){
echo fread($fileObj,10240);
ob_flush();
flush();
}
fclose($fileObj);
}
}
/**
* 有限速发送文件数据
*
* @param int $speed 最大速度
* @param boolean $download 是否为下载请求
* @return void
*/
public function outputWithLimit($speed,$download = false){
ignore_user_abort(false);
$filePath = ROOT_PATH . 'public/uploads/' . $this->fileModel["pre_name"];
set_time_limit(0);
session_write_close();
if($download){
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($filePath));
$encoded_fname = rawurlencode($this->fileModel["orign_name"]);
header('Content-Disposition: attachment;filename="'.$encoded_fname.'";filename*=utf-8'."''".$encoded_fname);
ob_flush();
flush();
}else{
header('Content-Type: '.self::getMimetype($filePath));
ob_flush();
flush();
}
if(file_exists($filePath)){
$fileObj = fopen($filePath,"r");
while (!feof($fileObj)){
echo fread($fileObj,round($speed*1024));
ob_flush();
flush();
sleep(1);
}
fclose($fileObj);
}
}
/**
* 获取文件MIME Type
*
* @param string $path 文件路径
* @return void
*/
static function getMimetype($path){
//FILEINFO_MIME will output something like "image/jpeg; charset=binary"
$finfoObj = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfoObj, $path);
finfo_close($finfoObj);
return $mimetype;
}
/**
* 获取文件MIME Type
*
* @param string $path 文件路径
* @return void
*/
static function getMimetypeOnly($path){
//FILEINFO_MIME_TYPE will output something like "image/jpeg"
$finfoObj = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfoObj, $path);
finfo_close($finfoObj);
return $mimetype;
}
/**
* 获取断点续传时HTTP_RANGE头
*
* @param int $file_size 文件大小
* @return void
*/
private function getRange($file_size){
if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){
$range = $_SERVER['HTTP_RANGE'];
$range = preg_replace('/[\s|,].*/', '', $range);
$range = explode('-', substr($range, 6));
if(count($range)<2){
$range[1] = $file_size;
}
$range = array_combine(array('start','end'), $range);
if(empty($range['start'])){
$range['start'] = 0;
}
if(empty($range['end'])){
$range['end'] = $file_size;
}
return $range;
}
return null;
}
/**
* 生成/返回 文件缩略图
*
* @return array 重定向信息
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$picInfo = self::getThumbSize($picInfo[0],$picInfo[1]);
if(file_exists(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb")){
self::outputThumb(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
return [0,0];
}
$thumbImg = new Thumb(ROOT_PATH . "public/uploads/".$this->fileModel["pre_name"]);
$thumbImg->thumb($picInfo[1], $picInfo[0]);
if(!is_dir(dirname(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]))){
mkdir(dirname(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]),0777,true);
}
$thumbImg->out(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
self::outputThumb(ROOT_PATH . "public/thumb/".$this->fileModel["pre_name"]."_thumb");
return [0,0];
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
// $rate = $width/$height;
// $maxWidth = 280;
// $maxHeight = 150;
// $changeWidth = 150*$rate;
// $changeHeight = 280/$rate;
// if($changeWidth>=$maxWidth){
// return [(int)$changeHeight,280];
// }
return [230,200];
}
/**
* 输出缩略图
*
* @param string $path 缩略图文件路径
* @return void
*/
static function outputThumb($path){
ob_end_clean();
if(!input("get.cache")=="no"){
header("Cache-Control: max-age=10800");
}
header('Content-Type: '.self::getMimetype($path));
$fileObj = fopen($path,"r");
echo fread($fileObj,filesize($path));
fclose($file);
}
/**
* 处理下载请求
*
* @param boolean $isAdmin 是否为管理员请求
* @return void
*/
public function Download($isAdmin=false){
$speedLimit = Db::name('groups')->where('id',$this->userModel["user_group"])->find();
$rangeTransfer = $speedLimit["range_transfer"];
$speedLimit = $speedLimit["speed"];
$sendFileOptions = Option::getValues(["download"]);
if($sendFileOptions["sendfile"] == "1"){
$this->sendFile($speedLimit,$rangeTransfer,true,$sendFileOptions["header"]);
}else{
if($isAdmin){
$speedLimit = "";
}
if($speedLimit == "0"){
exit();
}else if(empty($speedLimit)){
$this->outputWithoutLimit(true,$rangeTransfer);
exit();
}else if((int)$speedLimit > 0){
$this->outputWithLimit($speedLimit,true);
}
}
}
/**
* 删除指定本地文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$fileListTemp = array_column($fileList, 'pre_name');
foreach ($fileListTemp as $key => $value) {
@unlink(ROOT_PATH . 'public/uploads/'.$value);
if(file_exists(ROOT_PATH . 'public/thumb/'.$value."_thumb")){
@unlink(ROOT_PATH . 'public/thumb/'.$value."_thumb");
}
}
}
/**
* 签名临时直链,用于Office365预览
*
* @return array
*/
public function signTmpUrl(){
$options = Option::getValues(["oss","basic"]);
$timeOut = $options["timeout"];
$delayTime = time()+$timeOut;
$key=$this->fileModel["id"].":".$delayTime.":".md5($this->userModel["user_pass"].$this->fileModel["id"].$delayTime.config("salt"));
return [1,$options['siteURL']."Callback/TmpPreview/key/".$key];
}
}
?>
+4 -1
View File
@@ -45,13 +45,16 @@ class Mail extends Model{
$mail->Username =$this->smtpUser;
$mail->Password = $this->smtpPass;
$mail->From = $this->fromAdress;
$mail->SMTPDebug = 1;
$mail->Debugoutput = function($str, $level) {
$this->errorMsg .= $str;
};
$mail->isHTML(true);
$mail->addAddress($to,$name);
$mail->Subject = $title;
$mail->Body = $content;
$status = $mail->send();
if(!$status){
$this->errorMsg = $mail->ErrorInfo;
return false;
}
return true;
@@ -6,7 +6,7 @@ use think\Db;
use Sabre\DAV;
use \app\index\model\FileManage;
class Object extends DAV\File{
class Objects extends DAV\File{
private $myPath;
public $id;
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \Krizalys\Onedrive\Client;
use \app\index\model\Option;
/**
* Onedrive策略文件管理适配器
*/
class OnedriveAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
private $clinet;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
$this->clinet = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $this->policyModel["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($this->policyModel["sk"]),
]);
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
$file = new \Krizalys\Onedrive\File($this->clinet,"/me/drive/root:/".$this->fileModel["pre_name"].":");
return $file->fetchContent();
}
/**
* 签名预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
$preview = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":")),true);
return [1,$preview["@microsoft.graph.downloadUrl"]];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$this->clinet->createFile(rawurldecode($this->fileModel["pre_name"]),"/me/drive/root:/",$content);
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
$rate = $width/$height;
$maxWidth = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$thumbSize = self::getThumbSize($picInfo[0],$picInfo[1]);
$thumb = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":/thumbnails")),true);
return [1,$thumb["value"][0]["small"]["url"]];
}
/**
* 删除某一策略下的指定upyun文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$clinet = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
foreach (array_column($fileList, 'pre_name') as $key => $value) {
$clinet->deleteObject("/me/drive/root:/".rawurlencode($value).":");
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$preview = json_decode(json_encode($this->clinet->apiGet("/me/drive/root:/".rawurlencode($this->fileModel["pre_name"]).":")),true);
return [1,$preview["@microsoft.graph.downloadUrl"]];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+19 -1
View File
@@ -5,10 +5,28 @@ use think\Model;
use think\Db;
class Option extends Model{
static function getValues($groups = ['basic']){
static function getValues($groups = ['basic'],$userInfo=null){
$t = Db::name('options')->where('option_type','in',$groups)->column('option_value','option_name');
if(in_array("basic",$groups)){
return array_merge($t,self::getThemeOptions($t,$userInfo));
}
return $t;
}
static function getThemeOptions($basicOptions,$userInfo){
$themes = json_decode($basicOptions["themes"],true);
if($userInfo==null){
return ["themeColor"=>$basicOptions["defaultTheme"],"themeConfig"=>$themes[$basicOptions["defaultTheme"]]];
}else{
$userOptions = json_decode($userInfo["options"],true);
if(empty($userOptions)||!array_key_exists("preferTheme",$userOptions)||!array_key_exists($userOptions["preferTheme"],$themes)){
return ["themeColor"=>$basicOptions["defaultTheme"],"themeConfig"=>$themes[$basicOptions["defaultTheme"]]];
}
return ["themeColor"=>$userOptions["preferTheme"],"themeConfig"=>$themes[$userOptions["preferTheme"]]];
}
}
static function getValue($optionName){
return Db::name('options')->where('option_name',$optionName)->value('option_value');
}
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use OSS\OssClient;
use OSS\Core\OssException;
use \app\index\model\Option;
/**
* 阿里云OSS策略文件管理适配器
*/
class OssAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取OSS策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名OSS预览URL
*
* @return void
*/
public function Preview(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
return[true,$fileUrl];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 保存OSS文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = "http".ltrim(ltrim($this->policyModel["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
die('{ "result": { "success": false, "error": "鉴权失败" } }');
}
try{
$ossClient->putObject($this->policyModel["bucketname"], $this->fileModel["pre_name"], $content);
} catch(OssException $e) {
die('{ "result": { "success": false, "error": "编辑失败" } }');
}
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?x-oss-process=image/resize,m_lfit,h_39,w_90";
return[true,$fileUrl];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"),'GET', array("x-oss-process" => 'image/resize,m_lfit,h_39,w_90'));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 删除某一策略下的指定OSS文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
$accessKeyId = $policyData["ak"];
$accessKeySecret = $policyData["sk"];
$endpoint = "http".ltrim(ltrim($policyData["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return false;
}
try{
$ossClient->deleteObjects($policyData["bucketname"], array_column($fileList, 'pre_name'));
} catch(OssException $e) {
return false;
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
if(!$this->policyModel['bucket_private']){
return[true,"/File/OssDownload?url=".urlencode($this->policyModel["url"].$this->fileModel["pre_name"])."&name=".urlencode($this->fileModel["orign_name"])];
}else{
$accessKeyId = $this->policyModel["ak"];
$accessKeySecret = $this->policyModel["sk"];
$endpoint = $this->policyModel["url"];
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return [false,0];
}
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
try{
$signedUrl = $ossClient->signUrl($this->policyModel["bucketname"], $this->fileModel["pre_name"], Option::getValue("timeout"),'GET', array("response-content-disposition" => 'attachment; filename='.$this->fileModel["orign_name"]));
} catch(OssException $e) {
return [false,0];
}
return[true,$signedUrl];
}
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteOssFile($fname,$policy){
$accessKeyId = $policy["ak"];
$accessKeySecret = $policy["sk"];
$endpoint = "http".ltrim(ltrim($policy["server"],"https"),"http");
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, true);
} catch (OssException $e) {
return false;
}
try{
$ossClient->deleteObject($policy["bucketname"], $fname);
} catch(OssException $e) {
return false;
}
return true;
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+149
View File
@@ -0,0 +1,149 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
require_once 'extend/Qiniu/functions.php';
use Qiniu\Auth;
use \app\index\model\Option;
/**
* 七牛策略文件管理适配器
*/
class QiniuAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名七牛文件预览URL
*
* @param string $thumb 缩略图参数
* @return void
*/
public function Preview($thumb=null){
if($thumb===true || $thumb===false){
$thumb =null;
}
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"].$thumb;
return[true,$fileUrl];
}else{
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"].$thumb;
$signedUrl = $auth->privateDownloadUrl($baseUrl);
return[true,$signedUrl];
}
}
/**
* 保存七牛文件内容
*
* @param string $content 文件内容
* @return bool
*/
public function saveContent($content){
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$expires = 3600;
$keyToOverwrite = $this->fileModel["pre_name"];
$upToken = $auth->uploadToken($this->policyModel["bucketname"], $keyToOverwrite, $expires, null, true);
$uploadMgr = new \Qiniu\Storage\UploadManager();
list($ret, $err) = $uploadMgr->put($upToken, $keyToOverwrite, $content);
if ($err !== null) {
die('{ "result": { "success": false, "error": "编辑失败" } }');
} else {
return true;
}
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
return $this->Preview("?imageView2/1/w/230/h/200");
}
/**
* 删除某一策略下的指定七牛文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function deleteFile($fileList,$policyData){
$auth = new Auth($policyData["ak"], $policyData["sk"]);
$config = new \Qiniu\Config();
$bucketManager = new \Qiniu\Storage\BucketManager($auth);
$fileListTemp = array_column($fileList, 'pre_name');
$ops = $bucketManager->buildBatchDelete($policyData["bucketname"], $fileListTemp);
list($ret, $err) = $bucketManager->batch($ops);
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?attname=".urlencode($this->fileModel["orign_name"]);
return[true,$fileUrl];
}else{
$auth = new Auth($this->policyModel["ak"], $this->policyModel["sk"]);
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?attname=".urlencode($this->fileModel["orign_name"]);
$signedUrl = $auth->privateDownloadUrl($baseUrl);
return[true,$signedUrl];
}
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return void
*/
static function deleteSingle($fname,$policy){
$auth = new Auth($policy["ak"], $policy["sk"]);
$config = new \Qiniu\Config();
$bucketManager = new \Qiniu\Storage\BucketManager($auth);
$err = $bucketManager->delete($policy["bucketname"], $fname);
if ($err) {
return false;
}else{
return true;
}
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
class Remote extends Model{
public $sk;
private $policy;
private $serverOutput;
private $httpCode;
public function __construct($policy){
$this->policy = $policy;
}
public function remove($fileList){
$signKey = $this->sign($fileList,"DELETE");
$this->send("manager.php",$signKey,"DELETE",base64_encode(json_encode($fileList)));
}
public function preview($fname){
return $this->signUrl($this->policy["url"]."object.php?action=preview&name=".urlencode($fname)."&expires=".(time()+(int)Option::getValue("timeout")));
}
public function clean(){
return $this->signUrl($this->policy["url"]."object.php?action=clean&expires=".(time()+(int)Option::getValue("timeout")));
}
public function download($fname,$attnanme){
return $this->signUrl($this->policy["url"]."object.php?action=download&name=".urlencode($fname)."&attaname=".urlencode($attnanme)."&expires=".(time()+(int)Option::getValue("timeout")));
}
public function thumb($fname,$picInfo){
return $this->signUrl($this->policy["url"]."object.php?action=thumb&name=".urlencode($fname)."&expires=".(time()+(int)Option::getValue("timeout"))."&w=".$picInfo[0]."&h=".$picInfo[1]);
}
public function signUrl($url){
$signKey = hash_hmac("sha256",$url,"GET".$this->policy["sk"]);
return $url."&auth=".$signKey;
}
public function updateContent($fname,$content){
$object = ["fname"=>$fname,"content"=>$content];
$signKey = $this->sign($object,"UPDATE");
$this->send("manager.php",$signKey,"UPDATE",base64_encode(json_encode($object)));
}
public function send($target,$auth,$action,$object){
$session = curl_init($this->policy["server"].$target);
$postData = array(
"action" => $action,
"auth" => $auth,
"object" => $object,
);
curl_setopt($session, CURLOPT_POST, 1);
curl_setopt($session, CURLOPT_POSTFIELDS, $postData);
curl_setopt($session, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, false);
$this->serverOutput = curl_exec($session);
$this->httpCode = curl_getinfo($session,CURLINFO_HTTP_CODE);
echo $this->serverOutput;
}
public function sign($content,$method = null){
return hash_hmac("sha256",base64_encode(json_encode($content)),$method.$this->policy["sk"]);
}
}
?>
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
/**
* 远程策略文件管理适配器
*/
class RemoteAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名文件预览URL
*
* @return void
*/
public function Preview(){
$remote = new Remote($this->policyModel);
return [1,$remote->preview($this->fileModel["pre_name"])];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return bool
*/
public function saveContent($content){
$remote = new Remote($this->policyModel);
$remote->updateContent($this->fileModel["pre_name"],$content);
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$remote = new Remote($this->policyModel);
return [1,$remote->thumb($this->fileModel["pre_name"],explode(",",$this->fileModel["pic_info"]))];
}
/**
* 删除某一策略下的指定文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function deleteFile($fileList,$policyData){
$remoteObj = new Remote($policyData);
$remoteObj->remove(array_column($fileList, 'pre_name'));
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$remote = new Remote($this->policyModel);
return [1,$remote->download($this->fileModel["pre_name"],$this->fileModel["orign_name"])];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+166
View File
@@ -0,0 +1,166 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
/**
* AWS S3策略文件管理适配器
*/
class S3Adapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取又拍云策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名S3预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
if($base===true || $base ===false){
$base = null;
}
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
]);
$timeOut = Option::getValue("timeout");
$req = $s3->createPresignedRequest($cmd, '+'.($timeOut/60).' minutes');
$url = (string)$req->getUri();
return [1,$url];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$s3->putObject([
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
'Body' => $content,
]);
}
/**
* 删除某一策略下的指定文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
foreach (array_column($fileList, 'pre_name') as $key => $value) {
self::deleteS3File($value,$policyData);
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
$timeOut = Option::getValue("timeout");
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $this->policyModel["op_name"],
'endpoint' => $this->policyModel["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $this->policyModel["ak"],
'secret' => $this->policyModel["sk"],
],
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $this->policyModel["bucketname"],
'Key' => $this->fileModel["pre_name"],
'ResponseContentDisposition' => 'attachment; filename='.$this->fileModel["orign_name"],
]);
$timeOut = Option::getValue("timeout");
$req = $s3->createPresignedRequest($cmd, '+'.($timeOut/60).' minutes');
$url = (string)$req->getUri();
return [1,$url];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteS3File($fname,$policy){
$s3 = new \Aws\S3\S3Client([
'version' => 'latest',
'region' => $policy["op_name"],
'endpoint' => $policy["op_pwd"],
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $policy["ak"],
'secret' => $policy["sk"],
],
]);
$result = $s3->deleteObject([
'Bucket' => $policy["bucketname"],
'Key' => $fname,
]);
return $result["DeleteMarker"];
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+68 -10
View File
@@ -57,13 +57,15 @@ class ShareHandler extends Model{
"msg" => "无权操作"
);
}
$newPwd = self::getRandomKey(6);
Db::name('shares')->where('share_key',$this->shareData["share_key"])->update([
'type' => $this->shareData["type"] == "public"?"private":"public",
'share_pwd' => self::getRandomKey(6)
'share_pwd' => $newPwd
]);
return array(
"error" =>0,
"msg" => "更改成功"
"msg" => "更改成功",
"newPwd" => $newPwd,
);
}
@@ -81,6 +83,62 @@ class ShareHandler extends Model{
}
}
public function getThumb($user,$path,$folder=false){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
if($folder){
$fileObj = new FileManage($path,$this->shareData["owner"]);
}else{
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}
return $fileObj->getThumb();
}
public function getDocPreview($user,$path,$inFolder){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
if($inFolder){
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}else{
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
}
$tmpUrl = $fileObj->signTmpUrl();
return[true,"http://view.officeapps.live.com/op/view.aspx?src=".urlencode($tmpUrl)];
}
public function getContent($user,$path=null,$inFolder){
$checkStatus = $this->checkSession($user);
if(!$checkStatus[0]){
return [$checkStatus[0],$checkStatus[1]];
}
if($inFolder){
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$fileObj = new FileManage($reqPath["position_absolute"].$path,$this->shareData["owner"]);
}else{
$reqPath = Db::name('files')->where('id',$this->shareData["source_name"])->find();
if($reqPath["dir"] == "/"){
$reqPath["dir"] = $reqPath["dir"].$reqPath["orign_name"];
}else{
$reqPath["dir"] = $reqPath["dir"]."/".$reqPath["orign_name"];
}
$fileObj = new FileManage($reqPath["dir"],$this->shareData["owner"]);
}
$fileObj->getContent();
exit();
}
public function checkSession($user){
if($this->lockStatus){
return [false,"会话过期,请刷新页面"];
@@ -132,7 +190,7 @@ class ShareHandler extends Model{
}
$reqPath = Db::name('folders')->where('position_absolute',$this->shareData["source_name"])->find();
$path = $path == "/"?"":$path;
return FileManage::ListFile($this->shareData["source_name"].$path,$this->shareData["owner"]);
return FileManage::ListFile($this->shareData["source_name"].$path,$this->shareData["owner"],true,$this->shareData["source_name"]);
}
public function Preview($user){
@@ -221,7 +279,7 @@ class ShareHandler extends Model{
}
}
static function createShare($fname,$type,$user,$group){
static function createShare($fname,$type,$pwd,$user,$group){
if(!$group["allow_share"]){
self::setError("您当前的用户组无权分享文件");
}
@@ -229,9 +287,9 @@ class ShareHandler extends Model{
$fnameTmp = FileManage::getFileName($fname)[0];
$fileRecord = Db::name('files')->where('upload_user',$user["id"])->where('orign_name',$fnameTmp)->where('dir',$path)->find();
if(empty($fileRecord)){
self::createDirShare($fname,$type,$user,$group);
self::createDirShare($fname,$type,$pwd,$user,$group);
}else{
self::createFileShare($fileRecord,$type,$user,$group);
self::createFileShare($fileRecord,$type,$pwd,$user,$group);
}
}
@@ -243,13 +301,13 @@ class ShareHandler extends Model{
die('{ "result": "'.$text.'" }');
}
static function createDirShare($fname,$type,$user,$group){
static function createDirShare($fname,$type,$pwd,$user,$group){
$dirRecord = Db::name('folders')->where('owner',$user["id"])->where('position_absolute',$fname)->find();
if(empty($dirRecord)){
self::setError("目录不存在");
}
$shareKey = self::getRandomKey(8);
$sharePwd = $type=="public" ? "0" : self::getRandomKey(6);
$sharePwd = $type=="public" ? "0" : $pwd;
$SQLData = [
'type' => $type=="public" ? "public" : "private",
'share_time' => date("Y-m-d H:i:s"),
@@ -271,9 +329,9 @@ class ShareHandler extends Model{
}
}
static function createFileShare($file,$type,$user,$group){
static function createFileShare($file,$type,$pwd,$user,$group){
$shareKey = self::getRandomKey(8);
$sharePwd = $type=="public" ? "0" : self::getRandomKey(6);
$sharePwd = $type=="public" ? "0" : $pwd;
$SQLData = [
'type' => $type=="public" ? "public" : "private",
'share_time' => date("Y-m-d H:i:s"),
+412
View File
@@ -0,0 +1,412 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use \app\index\model\Option;
use \app\index\model\FileManage;
use think\console\Input;
use think\console\Output;
use \Krizalys\Onedrive\Client;
use Sabre\DAV\Mock\File;
class Task extends Model{
public $taskModel;
public $taskName;
public $taskType;
public $taskContent;
public $input;
public $output;
public $userId;
public $status = "success";
public $errorMsg;
public $policyModel;
public function __construct($id=null){
if($id!==null){
}
}
/**
* 保存任务至数据库
*
* @return void
*/
public function saveTask(){
Db::name("task")->insert([
"task_name" => $this->taskName,
"attr" => $this->taskContent,
"type" => $this->taskType,
"status" => "todo",
"uid" => $this->userId,
]);
}
/**
* 开始执行任务
*
* @return void
*/
public function Doit(){
switch ($this->taskModel["type"]){
case "uploadSingleToOnedrive":
$this->uploadSingleToOnedrive();
break;
case "UploadRegularRemoteDownloadFileToOnedrive":
$this->uploadSingleToOnedrive();
break;
case "uploadChunksToOnedrive":
$this->uploadChunksToOnedrive();
break;
case "UploadLargeRemoteDownloadFileToOnedrive":
$this->uploadUnchunkedFile();
break;
default:
$this->output->writeln("Unknown task type (".$this->taskModel["type"].")");
break;
}
}
/**
* 上传未分片的大文件至Onedrive
*
* @return void
*/
private function uploadUnchunkedFile(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//每次4MB上传文件
if(!$file = @fopen($this->taskContent["originPath"],"r")){
$this->status="error";
$this->errorMsg = "File not exist.";
$this->cleanTmpChunk();
return;
}
$offset = 0;
$totalSize = filesize($this->taskContent["originPath"]);
while (1) {
//移动文件指针
fseek($file, $offset);
$chunksize = (($offset+4*1024*1024)>$totalSize)?($totalSize-$offset):1024*4*1024;
$headers = [];
$headers[] = "Content-Length: ".$chunksize;
$headers[] = "Content-Range: bytes ".$offset."-".($offset+$chunksize-1)."/".$this->taskContent["fsize"];
//发送单个分片数据
try{
$onedrive->sendFileChunk($uploadUrl,$headers,fread($file,$chunksize));
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
$this->output->writeln("[Info] Chunk Uploaded. Offset:".$offset);
$offset+=$chunksize;
if($offset+1 >=$totalSize){
break;
}
}
fclose($file);
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]),$onedrive);
$this->cleanTmpChunk();
return;
}
$this->cleanTmpChunk();
}
/**
* 上传已分片的大文件至Onedrive
*
* @return void
*/
private function uploadChunksToOnedrive(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
//创建分片上传Session,获取上传URL
try{
$uploadUrl = $onedrive->apiPost("/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]).":/createUploadSession",[])->uploadUrl;
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
//逐个上传文件分片
$offset = 0;
foreach ($this->taskContent["chunks"] as $key => $value) {
$chunkPath = ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk";
if(!$file = @fopen($chunkPath,"r")){
$this->status="error";
$this->errorMsg = "File chunk not exist.";
$this->cleanTmpChunk();
return;
}
$headers = [];
$chunksize = filesize($chunkPath);
$headers[] = "Content-Length: ".$chunksize;
$headers[] = "Content-Range: bytes ".$offset."-".($offset+$chunksize-1)."/".$this->taskContent["fsize"];
//发送单个分片数据
try{
$onedrive->sendFileChunk($uploadUrl,$headers,$file);
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpChunk();
return;
}
$this->output->writeln("[Info] Chunk Uploaded. Offset:".$offset);
$offset += $chunksize;
fclose($file);
}
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".rawurlencode($this->taskContent["savePath"] . "/" . $this->taskContent["objname"]),$onedrive);
$this->cleanTmpChunk();
return;
}
$this->cleanTmpChunk();
}
/**
* 上传单文件(<=4mb)至Onedrive
*
* @return void
*/
private function uploadSingleToOnedrive(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$filePath = $this->taskModel["type"] == "UploadRegularRemoteDownloadFileToOnedrive"?$this->taskContent["originPath"]:ROOT_PATH . 'public/uploads/'.$this->taskContent["savePath"] . "/" . $this->taskContent["objname"];
if($file = @fopen($filePath,"r")){
try{
$onedrive->createFile(rawurlencode($this->taskContent["objname"]),"/me/drive/root:/".$this->taskContent["savePath"],$file);
}catch(\Exception $e){
$this->status="error";
$this->errorMsg = $e->getMessage();
$this->cleanTmpFile();
return;
}
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
return;
}
fclose($file);
$this->cleanTmpFile();
}else{
$this->status = "error";
$this->errorMsg = "Failed to open file [".$filePath."]";
}
}
/**
* 删除本地临时文件
*
* @return bool 是否成功
*/
private function cleanTmpFile(){
if($this->taskModel["type"] == "UploadRegularRemoteDownloadFileToOnedrive"){
return @unlink($this->taskContent["originPath"]);
}else{
return @unlink(ROOT_PATH . 'public/uploads/'.$this->taskContent["savePath"] . "/" . $this->taskContent["objname"]);
}
}
/**
* 删除本地临时分片
*
* @return void
*/
private function cleanTmpChunk(){
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive"){
@unlink($this->taskContent["originPath"]);
}else{
foreach ($this->taskContent["chunks"] as $key => $value) {
@unlink( ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
}
}
}
/**
* 设置为出错状态并清理远程文件
*
* @param string $msg 错误消息
* @param bool $delete 是否删除文件
* @param string $path 文件路径
* @param mixed $adapter 远程操作适配器
* @return void
*/
private function setError($msg,$delete,$path,$adapter){
$this->status="error";
$this->errorMsg = $msg;
if($delete){
switch($this->taskModel["type"]){
case "uploadSingleToOnedrive":
$adapter->deleteObject($path);
break;
case "uploadChunksToOnedrive":
$adapter->deleteObject($path);
break;
default:
break;
}
}
FileManage::storageGiveBack($this->taskModel["uid"],$this->taskContent["fsize"]);
}
public function setSuccess(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$jsonData = array(
"path" => $this->taskContent["path"],
"fname" => $this->taskContent["fname"],
"objname" => $this->taskContent["savePath"]."/".$this->taskContent["objname"],
"fsize" => $this->taskContent["fsize"],
);
$addAction = FileManage::addFile($jsonData,$policyData,$this->taskModel["uid"],$this->taskContent["picInfo"]);
if(!$addAction[0]){
$this->setError($addAction[1],true,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"error|".$this->errorMsg]);
return ["error"=>true,"msg"=>$this->errorMsg];
}
$this->cleanTmpFile();
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive" || $this->taskModel["type"] == "uploadChunksToOnedrive"){
$this->cleanTmpChunk();
}
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"complete"]);
return ["error"=>false,"msg"=>null];
}
public function Error(){
$this->taskContent = json_decode($this->taskModel["attr"],true);
$policyData = Db::name("policy")->where("id",$this->taskContent["policyId"])->find();
$this->policyModel = $policyData;
$onedrive = new Client([
'stream_back_end' => \Krizalys\Onedrive\StreamBackEnd::TEMP,
'client_id' => $policyData["bucketname"],
// Restore the previous state while instantiating this client to proceed in
// obtaining an access token.
'state' => json_decode($policyData["sk"]),
]);
$this->setError("",false,"/me/drive/root:/".$this->taskContent["savePath"]."/".rawurlencode($this->taskContent["objname"]),$onedrive);
$this->cleanTmpFile();
if($this->taskModel["type"] == "UploadLargeRemoteDownloadFileToOnedrive" || $this->taskModel["type"] == "uploadChunksToOnedrive"){
$this->cleanTmpChunk();
}
Db::name("task")->where("id",$this->taskModel["id"])->update(["status"=>"error"]);
}
}
?>
+7 -6
View File
@@ -20,15 +20,16 @@ class TwoFactor extends Model{
public function qrcodeRender(){
ob_end_clean();
header("content-type: image/png");
$this->secretKey = $this->ga->createSecret();
$qrCode = new QrCode();
session("two_factor_enable",$this->secretKey);
$qrCode->setText(urldecode($this->ga->getQRCodeGoogleUrl(Option::getValue("siteName"), $this->secretKey)))
->setPadding(5)
->render();
$qrCode = new QrCode(urldecode(str_replace("https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=","",$this->ga->getQRCodeGoogleUrl(Option::getValue("siteName"), $this->secretKey))));
$qrCode->setSize(165);
$qrCode->setMargin(0);
header('Content-Type: '.$qrCode->getContentType());
echo $qrCode->writeString();
}
public function confirmCode($key,$code){
$this->secretKey = $key;
if(empty($code)){
@@ -50,4 +51,4 @@ class TwoFactor extends Model{
}
}
?>
?>
+135 -15
View File
@@ -8,6 +8,7 @@ use think\Db;
use Qiniu\Auth;
use \app\index\model\Option;
use \app\index\model\FileManage;
use \app\index\model\Task;
class UploadHandler extends Model{
@@ -57,7 +58,7 @@ class UploadHandler extends Model{
$this->setError("空间容量不足",false);
}
FileManage::storageCheckOut($this->userId,$chunkSize);
if($chunkSize >=4195304){
if($chunkSize >config('upload.chunk_size')){
$this->setError("分片错误",false);
}
$chunkObj=fopen (ROOT_PATH . 'public/uploads/chunks/'.$this->chunkData["obj_name"].".chunk","w+");
@@ -75,13 +76,25 @@ class UploadHandler extends Model{
return 0;
}
/**
* 组合分片并生成最终文件
*
* @param array $ctx 文件片校验码
* @param string $fname 最终文件名
* @param string $path 储存目录
* @return void
*/
public function generateFile($ctx,$fname,$path){
$ctxTmp = explode(",",$ctx);
$chunks = Db::name('chunks')->where([
'ctx' => ["in",$ctxTmp],
])->order('id asc')->select();
$file = $this->combineChunks($chunks);
$this->filterCheck($file,$fname);
$file = null;
if($this->policyContent["policy_type"] != "onedrive"){
$file = $this->combineChunks($chunks);
}
$this->filterCheck($file,$fname,$chunks);
$suffixTmp = explode('.', $fname);
$fileSuffix = array_pop($suffixTmp);
if($this->policyContent['autoname']){
@@ -95,28 +108,67 @@ class UploadHandler extends Model{
if(file_exists($savePath.DS.$fileName)){
$this->setError("文件重名",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}
if(!@rename(ROOT_PATH . 'public/uploads/chunks/'.$file,$savePath.DS.$fileName)){
$this->setError("文件创建失败",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}else{
if($this->policyContent["policy_type"] == "onedrive"){
if($path == "ROOTDIR"){
$path = "";
}
$jsonData = array(
$task = new Task();
$task->taskName = "Upload Big File " . $fname . " to Onedrive";
$task->taskType = "uploadChunksToOnedrive";
$task->taskContent = json_encode([
"path" => $path,
"fname" => $fname,
"objname" => $generatePath."/".$fileName,
"objname" => $fileName,
"savePath" => $generatePath,
"fsize" => $this->fileSizeTmp,
);
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId);
"picInfo" => "",
"chunks" => $chunks,
"policyId" => $this->policyContent['id']
]);
$task->userId = $this->userId;
$task->saveTask();
echo json_encode(array("key" => $fname));
}else{
if(!@rename(ROOT_PATH . 'public/uploads/chunks/'.$file,$savePath.DS.$fileName)){
$this->setError("文件创建失败",true,$file,ROOT_PATH . 'public/uploads/chunks/');
}else{
if($path == "ROOTDIR"){
$path = "";
}
$jsonData = array(
"path" => $path,
"fname" => $fname,
"objname" => $generatePath."/".$fileName,
"fsize" => $this->fileSizeTmp,
);
@list($width, $height, $type, $attr) = getimagesize($savePath.DS.$fileName);
$picInfo = empty($width)?" ":$width.",".$height;
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId,$picInfo);
if(!$addAction[0]){
$this->setError($addAction[1],true,$fileName,$savePath);
}
echo json_encode(array("key" => $fname));
echo json_encode(array("key" => $fname));
}
}
}
public function filterCheck($file,$fname){
$fileSize = filesize(ROOT_PATH . 'public/uploads/chunks/'.$file);
protected function countTotalChunkSize($chunks){
$size = 0;
foreach ($chunks as $key => $value) {
$size += @filesize(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
}
return $size;
}
public function filterCheck($file,$fname,$chunks){
if($file !=null){
$fileSize = filesize(ROOT_PATH . 'public/uploads/chunks/'.$file);
}else{
$fileSize = $this->countTotalChunkSize($chunks);
}
$suffixTmp = explode('.', $fname);
$fileSuffix = array_pop($suffixTmp);
$allowedSuffix = explode(',', self::getAllowedExt(json_decode($this->policyContent["filetype"],true)));
@@ -131,6 +183,12 @@ class UploadHandler extends Model{
$this->fileSizeTmp = $fileSize;
}
/**
* 组合文件分片
*
* @param array $fname 文件分片数据库记录
* @return void
*/
public function combineChunks($fname){
$fileName = "file_".self::getRandomKey(8);
$fileObj=fopen (ROOT_PATH . 'public/uploads/chunks/'.$fileName,"a+");
@@ -140,8 +198,8 @@ class UploadHandler extends Model{
if(!$fileObj || !$chunkObj){
$this->setError("文件创建失败",false);
}
$content = fread($chunkObj, 4195304);
fwrite($fileObj, $content, 4195304);
$content = fread($chunkObj, config('upload.chunk_size'));
fwrite($fileObj, $content, config('upload.chunk_size'));
unset($content);
fclose($chunkObj);
unlink(ROOT_PATH . 'public/uploads/chunks/'.$value["obj_name"].".chunk");
@@ -181,12 +239,39 @@ class UploadHandler extends Model{
);
@list($width, $height, $type, $attr) = getimagesize(rtrim($savePath, DS).DS.$Uploadinfo->getSaveName());
$picInfo = empty($width)?" ":$width.",".$height;
//处理Onedrive等非直传
if($this->policyContent['policy_type'] == "onedrive"){
$task = new Task();
$task->taskName = "Upload" . $info["name"] . " to Onedrive";
$task->taskType = "uploadSingleToOnedrive";
$task->taskContent = json_encode([
"path" => $info["path"],
"fname" => $info["name"],
"objname" => $Uploadinfo->getSaveName(),
"savePath" => $generatePath,
"fsize" => $Uploadinfo->getSize(),
"picInfo" => $picInfo,
"policyId" => $this->policyContent['id']
]);
$task->userId = $this->userId;
$task->saveTask();
echo json_encode(array("key" => $info["name"]));
FileManage::storageCheckOut($this->userId,$jsonData["fsize"],$Uploadinfo->getInfo('size'));
return;
}
//向数据库中添加文件记录
$addAction = FileManage::addFile($jsonData,$this->policyContent,$this->userId,$picInfo);
if(!$addAction[0]){
$tmpFileName = $Uploadinfo->getSaveName();
unset($Uploadinfo);
$this->setError($addAction[1],true,$tmpFileName,$savePath);
}
//扣除容量
FileManage::storageCheckOut($this->userId,$jsonData["fsize"],$Uploadinfo->getInfo('size'));
echo json_encode(array("key" => $info["name"]));
}else{
@@ -219,6 +304,9 @@ class UploadHandler extends Model{
case 'local':
return $this->getLocalToken();
break;
case 'onedrive':
return 'nazGTT91tboaLWBC549$:tHSsNyTBxoV4HDfELJeKH1EUmEY=:eyJjYWxsYmFja0JvZHkiOiJ7XCJwYXRoXCI6XCJcIn0iLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb25cL2pzb24iLCJzY29wZSI6ImMxNjMyMTc3LTQ4NGEtNGU1OS1hZDBhLWUwNDc4ZjZhY2NjZSIsImRlYWRsaW5lIjoxNTM2ODMxOTEwfQ==';
break;
case 'oss':
return $this->getOssToken();
break;
@@ -228,6 +316,9 @@ class UploadHandler extends Model{
case 's3':
return $this->getS3Token();
break;
case 'remote':
return $this->getRemoteToken();
break;
default:
# code...
break;
@@ -295,6 +386,35 @@ class UploadHandler extends Model{
return $token;
}
private function getRemoteToken(){
$callbackKey = $this->getRandomKey();
$sqlData = [
'callback_key' => $callbackKey,
'pid' => $this->policyId,
'uid' => $this->userId
];
Db::name('callback')->insert($sqlData);
$policy = array(
'callbackUrl' =>Option::getValue("siteURL").'Callback/Remote',
'callbackKey' => $callbackKey,
'callbackBodyType' => 'application/json',
'fsizeLimit' => (int)$this->policyContent['max_size'],
'uid' => $this->userId,
);
$dirName = $this->getObjName($this->policyContent['dirrule']);
if($this->policyContent["autoname"]){
$policy = array_merge($policy,array("saveKey" => $dirName.(empty($dirName)?"":"/").$this->getObjName($this->policyContent['namerule'])));
}else{
$policy = array_merge($policy,array("saveKey" => $dirName.(empty($dirName)?"":"/")."$(fname)"));
}
if(!empty($this->policyContent['mimetype'])){
$policy = array_merge($policy,array("mimeLimit" => $this->policyContent['mimetype']));
}
$signingKey = hash_hmac("sha256",json_encode($policy),"UPLOAD".$this->policyContent['sk']);
$token = $signingKey. ":" .base64_encode(json_encode($policy));
return $token;
}
static function upyunSign($key, $secret, $method, $uri, $date, $policy=null, $md5=null){
$elems = array();
foreach (array($method, $uri, $date, $policy, $md5) as $v){
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Upyun\Upyun;
use Upyun\Config;
use \app\index\model\Option;
/**
* 又拍云策略文件管理适配器
*/
class UpyunAdapter extends Model{
private $fileModel;
private $policyModel;
private $userModel;
public function __construct($file,$policy,$user){
$this->fileModel = $file;
$this->policyModel = $policy;
$this->userModel = $user;
}
/**
* 获取又拍云策略文本文件内容
*
* @return string 文件内容
*/
public function getFileContent(){
return file_get_contents($this->Preview()[1]);
}
/**
* 签名又拍云预览URL
*
* @return void
*/
public function Preview($base=null,$name=null){
if($base===true || $base===false){
$base =null;
}
if(!$this->policyModel['bucket_private']){
$fileUrl = $this->policyModel["url"].$this->fileModel["pre_name"]."?auth=0";
if(!empty($base)){
$fileUrl = $base;
}
return[true,$fileUrl];
}else{
$baseUrl = $this->policyModel["url"].$this->fileModel["pre_name"];
if(!empty($base)){
$baseUrl = $base;
}
$etime = time() + Option::getValue("timeout");
$key = $this->policyModel["sk"];
$path = "/".$this->fileModel["pre_name"];
if(!empty($name)){
$path = "/".$name;
}
$sign = substr(md5($key.'&'.$etime.'&'.$path), 12, 8).$etime;
$signedUrl = $baseUrl."?_upt=".$sign;
return[true,$signedUrl];
}
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$bucketConfig = new Config($this->policyModel["bucketname"], $this->policyModel["op_name"], $this->policyModel["op_pwd"]);
$client = new Upyun($bucketConfig);
if(empty($content)){
$content = " ";
}
$res=$client->write($this->fileModel["pre_name"],$content);
}
/**
* 计算缩略图大小
*
* @param int $width 原始宽
* @param int $height 原始高
* @return array
*/
static function getThumbSize($width,$height){
$rate = $width/$height;
$maxWidth = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 获取缩略图地址
*
* @return string 缩略图地址
*/
public function getThumb(){
$picInfo = explode(",",$this->fileModel["pic_info"]);
$thumbSize = self::getThumbSize($picInfo[0],$picInfo[1]);
$baseUrl =$this->policyModel["url"].$this->fileModel["pre_name"]."!/fwfh/90x39";
return [1,$this->Preview($baseUrl,$this->fileModel["pre_name"]."!/fwfh/90x39")[1]];
}
/**
* 删除某一策略下的指定upyun文件
*
* @param array $fileList 待删除文件的数据库记录
* @param array $policyData 待删除文件的上传策略信息
* @return void
*/
static function DeleteFile($fileList,$policyData){
foreach (array_column($fileList, 'pre_name') as $key => $value) {
self::deleteUpyunFile($value,$policyData);
}
}
/**
* 生成文件下载URL
*
* @return array
*/
public function Download(){
return [true,$this->Preview()[1]."&_upd=".urlencode($this->fileModel["orign_name"])];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteUpyunFile($fname,$policy){
$bucketConfig = new Config($policy["bucketname"], $policy["op_name"], $policy["op_pwd"]);
$client = new Upyun($bucketConfig);
$res=$client->delete($fname,true);
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+20 -14
View File
@@ -3,8 +3,6 @@ namespace app\index\model;
use think\Model;
use think\Db;
use \think\Session;
use \think\Cookie;
use \app\index\model\Option;
use \app\index\model\Mail;
use think\Validate;
@@ -87,7 +85,7 @@ class User extends Model{
* @return [type] [description]
*/
static function register($userEmail,$userPass,$captchaCode){
if(Option::getValue("login_captcha")=="1"){
if(Option::getValue("reg_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
@@ -97,7 +95,7 @@ class User extends Model{
}
$userName = str_replace(" ", "", $userEmail);
$passWord = $userPass;
if ( !filter_var($userName,FILTER_VALIDATE_EMAIL) || (mb_strlen($userName,'UTF8')>22) || (mb_strlen($userName,'UTF8')<4) || (mb_strlen($passWord,'UTF8')>64) || (mb_strlen($passWord,'UTF8')<4)){
if ( !filter_var($userName,FILTER_VALIDATE_EMAIL) || (mb_strlen($userName,'UTF8')>32) || (mb_strlen($userName,'UTF8')<4) || (mb_strlen($passWord,'UTF8')>64) || (mb_strlen($passWord,'UTF8')<4)){
return [false,"邮箱或密码不符合规范"];
}
if(Db::name('users')->where('user_email',$userName)->find() !=null){
@@ -127,6 +125,7 @@ class User extends Model{
'delay_time' =>0,
'avatar' => "default",
'profile' => true,
'options' => "{}",
];
if(Db::name('users')->insert($sqlData)){
$userId = Db::name('users')->getLastInsID();
@@ -193,7 +192,7 @@ class User extends Model{
}
static function findPwd($email,$captchaCode){
if(Option::getValue("login_captcha")=="1"){
if(Option::getValue("forget_captcha")=="1"){
if(!self::checkCaptcha($captchaCode)){
return [false,"验证码错误"];
}
@@ -308,23 +307,23 @@ class User extends Model{
public function getMemory($notEcho = false){
$usedMemory = $this->userSQLData["used_storage"];
$groupStorage = (int)$this->groupData["max_storage"];
$packetStorage = (int)Db::name('storage_pack')
$groupStorage = $this->groupData["max_storage"];
$packetStorage = Db::name('storage_pack')
->where('uid',$this->uid)
->where('dlay_time',">",time())
->sum('pack_size');
$returnData["used"] = self::countSize((int)$usedMemory);
$returnData["used"] = self::countSize($usedMemory);
$returnData["total"] = self::countSize($groupStorage+$packetStorage);
$returnData["rate"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["basic"] = self::countSize((int)$groupStorage);
$returnData["pack"] = self::countSize((int)$packetStorage);
$returnData["rate"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["basic"] = self::countSize($groupStorage);
$returnData["pack"] = self::countSize($packetStorage);
if($usedMemory > $groupStorage){
$returnData["r1"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = 0;
$returnData["r3"] = 100-$returnData["r1"];
}else{
$returnData["r1"] = floor((int)$usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = floor(((int)$groupStorage-$usedMemory)/($groupStorage+$packetStorage)*100);;
$returnData["r1"] = floor($usedMemory/($groupStorage+$packetStorage)*100);
$returnData["r2"] = floor(($groupStorage-$usedMemory)/($groupStorage+$packetStorage)*100);;
$returnData["r3"] = 100-$returnData["r1"]-$returnData["r2"];
}
if($notEcho){
@@ -355,6 +354,13 @@ class User extends Model{
}
}
public function changeOption($optionKey,$optionValue){
$options = json_decode($this->userSQLData["options"],true);
$options[$optionKey] = $optionValue;
Db::name("users")->where("id",$this->uid)->update(["options" => json_encode($options)]);
return [1,1];
}
public function changePwd($origin,$new){
if(md5(config('salt').$origin) != $this->userSQLData["user_pass"]){
return [0,"原密码错误"];
@@ -113,6 +113,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0" checked>
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略,开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>
+23 -3
View File
@@ -67,11 +67,31 @@
<img class="card-img-top" src="/static/img/s3.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Amazon S3</h5>
<p class="card-text"><a href="https://aws.amazon.com/cn/s3/?nc1=h_ls" target="_blank">Amazon S3</a>专为从任意位置存储和检索任意数量的数据而构建的对象存储</p>
<p class="card-text"><a href="https://aws.amazon.com/cn/s3/?nc1=h_ls" target="_blank">Amazon S3</a>专为数据而构建的对象存储</p>
<a href="/Admin/PolicyAddS3" class="btn btn-primary">添加</a>
</div>
</div>
</div>
<div class="col-md-3 mt-2 md-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/remote.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">远程服务器</h5>
<p class="card-text">你可以将文件存放在不同于主程序的服务器上。<a href="https://github.com/HFO4/Cloudreve/wiki/%E8%BF%9C%E7%A8%8B%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%90%AD%E5%BB%BA">配置说明</a></p>
<a href="/Admin/PolicyAddRemote" class="btn btn-primary">添加</a>
</div>
</div>
</div>
<div class="col-md-3 mt-2 md-2">
<div class="card cloud">
<img class="card-img-top" src="/static/img/onedrive.png" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Onedrive <span class="badge badge-warning">中转</span></h5>
<p class="card-text">支持 Onedrive Bussiness 和个人版。<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">配置说明</a></p>
<a href="/Admin/PolicyAddOnedrive" class="btn btn-primary">添加</a>
</div>
</div>
</div>
</div>
</div>
@@ -430,7 +450,7 @@
<label for="url" class="col-form-label col-form-label-sm">空间域名</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" required>
<input type="text" class="form-control" name="url" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间绑定的域名,用于文件下载、访问,结尾要加"/"(推荐配合阿里云CDN使用,此处填写CDN域名)</div>
</div>
@@ -440,7 +460,7 @@
<label for="server" class="col-form-label col-form-label-sm">上传域名</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" required>
<input type="text" class="form-control" name="server" value="http(s)://" required>
</div>
<div class="col-md-4 option-des"> OSS空间的域名,一般与上一项保持一致。(如果上一项填写的是CDN域名,则此处与上一项不应相同)</div>
</div>
@@ -0,0 +1,155 @@
{extend name="header_admin" /}
{block name="title"}添加上传策略- {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item">
<a href="/Admin/PolicyAdd">上传策略</a>
</li>
<li class="breadcrumb-item active">添加</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>添加上传策略</h2>
<br>
<div class="alert alert-warning" role="alert">
<strong>添加Onedrive上传策略前请确保阅读完下面内容!</strong>
<ul>
<li>Onedrive策略无法直传,上传文件时,Cloudreve会先将文件暂存在服务器,之后再由任务队列上传至Onedrive;</li>
<li>使用本策略前,请先配置并运行<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">Cloudreve任务队列</a>,否则正常无法使用;</li>
<li>用户上传文件后不能立马看到所上传的文件,需要等待任务队列处理完毕;</li>
<li>请确保已经正确配置好Cloudreve的Cron定时任务,以便用于刷新AccessToken信息。</li>
</ul>
</div>
<div class="card" id="s3" >
<div class="card-header">
添加Onedrive上传策略
</div>
<div class="card-body">
<form id="onedrivePolicy">
<input type="text" class="form-control" name="policy_type" value="onedrive" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="bucketname" class="col-form-label col-form-label-sm">应用 IDClient ID </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" required></div>
<div class="col-md-4 option-des"><a href="https://apps.dev.microsoft.com/?referrer=https%3a%2f%2fdeveloper.microsoft.com%2fzh-CN%2fgraph%2fquick-start#/quickstart/graphIO?publicClientSupport=false&appName=Cloudreve&redirectUrl={:urlencode($options.siteURL)}Admin%2FoneDriveCalllback&allowImplicitFlow=false&ru=https:%2F%2Fdeveloper.microsoft.com%2Fzh-CN%2Fgraph%2Fquick-start%3FappID%3D_appId_%26appName%3D_appName_%26redirectUrl%3D{$options.siteURL}Admin/oneDriveCalllback%26platform%3Doption-php" target="_blank">点击这里</a>获取下一步要用到的应用机密和此处的应用ID</div>
</div>
<input type="text" class="form-control" name="sk" value="0" style="display: none" required>
<input type="text" name="bucket_private" value="1" style="display: none">
<input type="text" name="origin_link" value="0" style="display: none">
<input type="text" name="url" value="0" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">应用机密(密码/公钥)</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="ak" required>
</div>
<div class="col-md-4 option-des"> 同上一项一起获取</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="10" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" selected>MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" >
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{uid}_{randomkey8}_{originname}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
Onedrive根目录
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用目录变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存并继续</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
<br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/add_policy.js"></script>
{/block}
@@ -0,0 +1,170 @@
{extend name="header_admin" /}
{block name="title"}添加上传策略- {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item">
<a href="/Admin/PolicyAdd">上传策略</a>
</li>
<li class="breadcrumb-item active">添加</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>添加上传策略</h2>
<br>
<div class="card" id="s3" >
<div class="card-header">
添加远程上传策略
</div>
<div class="card-body">
<form id="qiniuPolicy">
<input type="text" class="form-control" name="policy_type" value="remote" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="ak" class="col-form-label col-form-label-sm">AccessToken </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" required></div>
<div class="col-md-4 option-des"> 默认随机生成,请与远程服务端配置文件中保持一致</div>
</div>
<input type="text" class="form-control" name="ak" value="0" style="display: none" required>
<input type="text" name="bucket_private" value="1" style="display: none">
<input type="text" name="origin_link" value="0" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">服务端URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" required>
</div>
<div class="col-md-4 option-des"> 结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">下载根URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" required>
</div>
<div class="col-md-4 option-des"> 一般与上一步保持一致,结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="10" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" selected>MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" >
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{uid}_{randomkey8}_{originname}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
/public/uploads/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{date}/{uid}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用目录变量对照表中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
<br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/add_policy.js"></script>
<script type="text/javascript">
function randomString(len) {
  len = len || 32;
  var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  var maxPos = $chars.length;
  var pwd = '';
  for (i = 0; i < len; i++) {
    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return pwd;
}
$("[name='sk']").val(randomString(32));
</script>
{/block}
@@ -11,7 +11,14 @@
<li class="breadcrumb-item active">数据概况</li>
</ol>
<!-- Icon Cards-->
{eq name="isCronOk" value="true"}
{else/}
<div class="alert alert-danger" role="alert">
<strong>警告!Cron定时任务疑似未执行</strong><p>检测到<a href="/Admin/Cron">定时任务</a>未正常执行,请检查您的定时任务配置。您可以通过Crontab或者其他网站监控服务,让 “您的域名/Cron” 此地址每隔一段时间被访问一次。</p>
</div>
{/eq}
<div class="row">
<div class="col-xl-3 col-sm-6 mb-3">
<div class="card text-white bg-primary o-hidden h-100">
<div class="card-body">
+98
View File
@@ -0,0 +1,98 @@
{extend name="header_admin" /}
{block name="title"}基础设置- {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">配色方案</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>配色方案</h2>
<div class="alert alert-primary" role="alert">
<ul>
<li>你可以使用<a href="https://theme.cloudreve.org">Cloudreve配色工具</a>创建配色,并将生成的配置信息填写在下方;</li>
<li>主色调为不同配色的区分标识,请不要创建主色调相同的配色方案。</li>
</ul>
</div>
<div class="row">
<div class="col-md-9">
<form id="colorForm">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">默认配色方案</label>
</div>
<div class="col-md-4"><select class="form-control" name="header">
{volist name="colors" id="theme"}
<option value="{$key}" {eq name="optionsForSet.defaultTheme" value="$key"}selected{/eq}>{$key}</option>
{/volist}
</select></div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">可选配色方案</label>
</div>
<div class="col-md-8">
<table class="table" id="colorTable">
<thead>
<tr>
<th scope="col">主色调</th>
<th scope="col">颜色配置</th>
</tr>
</thead>
<tbody>
{volist name="colors" id="theme"}
<tr>
<th scope="row" style="color:{$key}">{$key}
<br><button id="removeRow" type="button" data-action="removeRow" class="btn btn-default">删除</button>
</th>
<td><textarea class="form-control" rows="4" name="color[]">{:json_encode($theme)}</textarea></td>
</tr>
{/volist}
</tbody>
</table>
<div style="text-align:right;"><button id="addRow" type="button" class="btn btn-default">增加一行</button></div>
</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-primary" id="saveColor">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
<br><br>
</div>
<div class="col-md-3">
<div class="file_side">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/color.js"></script>
<script type="text/javascript">
</script>
{/block}
+122
View File
@@ -0,0 +1,122 @@
{extend name="header_admin" /}
{block name="title"}离线下载 - {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">离线下载</li>
<li class="breadcrumb-item active">配置</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>离线下载</h2>
<br>
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#options"><i class="fa fa-cog" aria-hidden="true"></i> Aria2配置</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list"><i class="fa fa-list" aria-hidden="true"></i> 任务列表</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" >
<div class="tab-pane fade show active" id="options" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="aria2Options">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Server</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_rpcurl" value="{$optionsForSet.aria2_rpcurl}" spellcheck="false"></div>
<div class="col-md-4 option-des"> aria2的RPC服务器地址,请在aria2的配置文件中启用RPC服务。例如:http://127.0.0.1:6800/</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">RPC Token</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_token" value="{$optionsForSet.aria2_token}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 在配置文件中设置的RPC服务的Token</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">下载临时目录</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="aria2_tmppath" value="{$optionsForSet.aria2_tmppath}" spellcheck="false"></div>
<div class="col-md-4 option-des"> 下载文件数据的临时存放目录,请确保PHP对该目录拥有读写权限</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="aria2_options" class="col-form-label col-form-label-sm">其他下载参数</label>
</div>
<div class="col-md-4"> <textarea class="form-control" name="aria2_options" spellcheck="false">{$optionsForSet.aria2_options}</textarea></div>
<div class="col-md-4 option-des"> aria2启动下载的其他附带参数,请以json格式书写。你可也可以将这些设置写在aria2配置文件里。可用参数请查阅<a href="https://aria2.github.io/manual/en/html/aria2c.html#options" target="_blank">官方文档</a></div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-primary" id="saveAria2">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="list" role="tabpanel" aria-labelledby="pills-profile-tab">
<table class="table">
<thead>
<tr>
<th scope="col" class="textCenter">#</th>
<th scope="col" width="50%">文件名</th>
<th scope="col" class="textCenter">创建者</th>
<th scope="col" class="textCenter">文件大小</th>
<th scope="col" class="textCenter">状态</th>
<th scope="col" class="textCenter">进度</th>
<th scope="col" class="textCenter">操作</th>
</tr>
</thead>
<tbody id="listContent">
{volist name='list' id='download'}
<tr id="i-{$download.id}" data-pid="{$download.pid}">
<th scope="row" class="textCenter">{$download.id}</th>
<td>{:is_array($originList[$key]['fileName'])?"未知":$originList[$key]['fileName']}</td>
<td class="textCenter">{:$originList[$key]['user']["user_nick"]}</td>
<td class="textCenter">{:countSize($download.total_size)}</td>
<td class="textCenter">{$download.status}</td>
<td class="textCenter">{eq name="$originList[$key]['totalLength']" value="0"}-{else/}{:floor($originList[$key]['completedLength']/$originList[$key]['totalLength']*10000)/100}%{/eq}</td>
<td class="textCenter">{eq name="download.status" value="active"}<a href="javascript:" onclick="cancel('{$download.id}')">取消任务</a>{else/} - {/eq}</td>
</tr>
{/volist}
</tbody>
</table>
{$list->render()}
</div>
<div class="tab-pane fade" id="tools" role="tabpanel" aria-labelledby="pills-profile-tab">
ddd
</div>
</div>
</div>
</div><br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/summernote.min.js"></script>
<script src="/static/js/admin/summernote-zh-CN.min.js"></script>
<script src="/static/js/admin/aria2.js"></script>
{/block}
+18 -1
View File
@@ -114,6 +114,18 @@
<div class="col-md-4 option-des"> 是否允许用户使用WebDAV协议同步文件。目前此功能仅支持本地上传方案</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">离线下载</label>
</div>
<div class="col-md-4"> <input class="" type="radio" name="aria2" value="1" id="webdav1" >
<label class="" for="webdav1" >允许</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="aria2" id="webdav0" value="0">
<label class="" for="webdav0">禁止</label></div>
<div class="col-md-4 option-des"> 是否允许用户使用离线下载。此功能仅支持本地存储策略,开启前需要到离线下载管理页面设置Aria2接口</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="colFormLabelSm" class="col-form-label col-form-label-sm">用户组标志色</label>
@@ -162,10 +174,15 @@
<script src="/static/js/admin/add_group.js"></script>
<script type="text/javascript">
$("option[value='{$group.policy_name}']").attr("selected",true);
$("option[value='{$group.max_storage}']").attr("selected",true);
$("option[value='{:countSize($group.max_storage,true)[1]}']").attr("selected",true);
$("input[name='range_transfer'][value='{$group.range_transfer}']").attr("checked",true);
$("input[name='allow_share'][value='{$group.allow_share}']").attr("checked",true);
$("input[name='color'][value='{$group.color}']").attr("checked",true);
$("input[name='webdav'][value='{$group.webdav}']").attr("checked",true);
if('{$group.aria2}'=="1,1,1"){
$("input[name='aria2'][value='1']").attr("checked",true);
}else{
$("input[name='aria2'][value='0']").attr("checked",true);
}
</script>
{/block}
+247 -1
View File
@@ -684,7 +684,7 @@
{case value="s3"}
<div class="card" id="s3" >
<div class="card-header">
添加S3上传策略
编辑S3上传策略
</div>
<div class="card-body">
<form id="Policy">
@@ -804,6 +804,252 @@
</div>
{/case}
{case value="remote"}
<div class="card" id="remote" >
<div class="card-header">
编辑远程上传策略
</div>
<div class="card-body">
<form id="Policy">
<input type="text" value="{$policy.id}" name="id" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" value="{$policy.policy_name}" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="ak" class="col-form-label col-form-label-sm">AccessToken </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="sk" value="{$policy.sk}" required></div>
<div class="col-md-4 option-des"> 默认随机生成,请与远程服务端配置文件中保持一致</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">服务端URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="server" value="{$policy.server}" required>
</div>
<div class="col-md-4 option-des"> 结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">下载根URL</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="url" value="{$policy.url}" required>
</div>
<div class="col-md-4 option-des"> 一般与上一步保持一致,结尾要加"/"</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="{:countSize($policy.max_size,true)[0]}" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn" data-value="{:countSize($policy.max_size,true)[1]}">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" >MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
{eq name="$policy.autoname" value="1"}
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
{else/}
<input class="" type="radio" name="autoname" id="autoname1" value="1" >
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" checked>
<label class="" for="autoname2" >关闭</label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" style=" {eq name="$policy.autoname" value="0"}display:none{/eq}">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{$policy.namerule}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTable">变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
/public/uploads/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{$policy.dirrule}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTableFolder">目录变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="savePolicy">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
{/case}
{case value="onedrive"}
<div class="alert alert-warning" role="alert">
<strong>添加Onedrive上传策略前请确保阅读完下面内容!</strong>
<ul>
<li>Onedrive策略无法直传,上传文件时,Cloudreve会先将文件暂存在服务器,之后再由任务队列上传至Onedrive;</li>
<li>使用本策略前,请先配置并运行<a href="https://github.com/HFO4/Cloudreve/wiki/Onedrive%E5%AF%B9%E6%8E%A5%E8%AF%B4%E6%98%8E">Cloudreve任务队列</a>,否则正常无法使用;</li>
<li>用户上传文件后不能立马看到所上传的文件,需要等待任务队列处理完毕;</li>
<li>请确保已经正确配置好Cloudreve的Cron定时任务,以便用于刷新AccessToken信息。</li>
</ul>
</div>
<div class="card" id="s3" >
<div class="card-header">
添加Onedrive上传策略
</div>
<div class="card-body">
<form id="Policy">
<input type="text" value="{$policy.id}" name="id" style="display: none">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="policy_name" class="col-form-label col-form-label-sm">上传策略名称</label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" value="{$policy.policy_name}" name="policy_name" required></div>
<div class="col-md-4 option-des"> 上传策略的名称,用于区别不同策略</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="bucketname" class="col-form-label col-form-label-sm">应用 IDClient ID </label>
</div>
<div class="col-md-4"> <input type="text" class="form-control" name="bucketname" value="{$policy.bucketname}" required></div>
<div class="col-md-4 option-des"><a href="https://apps.dev.microsoft.com/?referrer=https%3a%2f%2fdeveloper.microsoft.com%2fzh-CN%2fgraph%2fquick-start#/quickstart/graphIO?publicClientSupport=false&appName=Cloudreve&redirectUrl={:urlencode($options.siteURL)}Admin%2FoneDriveCalllback&allowImplicitFlow=false&ru=https:%2F%2Fdeveloper.microsoft.com%2Fzh-CN%2Fgraph%2Fquick-start%3FappID%3D_appId_%26appName%3D_appName_%26redirectUrl%3D{$options.siteURL}Admin/oneDriveCalllback%26platform%3Doption-php" target="_blank">点击这里</a>获取下一步要用到的应用机密和此处的应用ID</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">应用机密(密码/公钥)</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="ak" value="{$policy.ak}" required>
</div>
<div class="col-md-4 option-des"> 同上一项一起获取</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">单文件最大大小</label>
</div>
<div class="col-md-4 input-group mb-3">
<input type="number" class="form-control" name="max_size" spellcheck="false" min="0" value="{:countSize($policy.max_size,true)[0]}" required>
<div class="input-group-append">
<span class="input-group-text" id="basic-addon2">
<select name="sizeTimes" class="selectIn" data-value="{:countSize($policy.max_size,true)[1]}">
<option value="1">B</option>
<option value="1024">KB</option>
<option value="1048576" >MB</option>
<option value="1073741824">GB</option>
</select>
</span>
</div>
</div>
<div class="col-md-4 option-des"> 允许上传的单个文件的最大尺寸</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">文件重命名</label>
</div>
<div class="col-md-4">
{eq name="$policy.autoname" value="1"}
<input class="" type="radio" name="autoname" id="autoname1" value="1" checked>
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" >
<label class="" for="autoname2">关闭</label>
{else/}
<input class="" type="radio" name="autoname" id="autoname1" value="1" >
<label class="" for="autoname1" >开启</label>
&nbsp;&nbsp;&nbsp;
<input class="" type="radio" name="autoname" id="autoname2" value="0" checked>
<label class="" for="autoname2" >关闭</label>
{/eq}
</div>
<div class="col-md-4 option-des"> 是否对存储的文件自动重命名。推荐开启,重命名不会影响用户端文件名展示,开启后可以避免文件重名</div>
</div>
<div class="row form-setting" id="autoname_form" style=" {eq name="$policy.autoname" value="0"}display:none{/eq}">
<div class="col-md-1 form-label ">
<label for="url" class="col-form-label col-form-label-sm">重命名规则</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="namerule" value="{$policy.namerule}" spellcheck="false" required>
</div>
<div class="col-md-4 option-des"> 你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTable">变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="filetype" class="col-form-label col-form-label-sm">存储目录</label>
</div>
<div class="col-md-4 input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2">
Onedrive根目录/
</span>
</div>
<input type="text" class="form-control" name="dirrule" spellcheck="false" value="{$policy.dirrule}" required>
</div>
<div class="col-md-4 option-des"> 文件存放的目录,你可以使用<a href="javascript:void()" data-toggle="modal" data-target="#varTableFolder">目录变量对照表</a>中的字段填写</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="submit" class="btn btn-primary" id="saveQiniu">保存</button> <a class="btn btn-danger" href="/Admin/UpdateOnedriveToken?id={$policy.id}" id="saveQiniu">重新授权账号(请先保存)</a></div>
<div class="col-md-4 option-des"> </div>
<br> <br>
</div>
</form>
</div>
<bn>
</div>
{/case}
{/switch}
<br>
</div>
@@ -103,7 +103,7 @@
</div>
</form>
</div>
<div class="tab-pane fade" id="test" role="tabpanel" aria-labelledby="pills-profile-tab">
<form id="testMail">
<div class="row form-setting">
@@ -145,7 +145,7 @@
<div class="tab-pane fade" id="template" role="tabpanel" aria-labelledby="pills-profile-tab">
<form id="mailTemplate">
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
@@ -66,6 +66,37 @@
<div class="col-md-4 option-des"> 用于展示用户头像的Gravatar服务器地址</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="gravatar_server" class="col-form-label col-form-label-sm">文件数据发送模式</label>
</div>
<div class="col-md-4">
<select class="form-control" name="sendfile">
<option value="0">传统</option>
<option value="1" {eq name="optionsForSet.sendfile" value="1"}selected{/eq}>X-Sendfile</option>
</select>
</div>
<div class="col-md-4 option-des"> 推荐使用X-Sendfile以获得高效的文件传输,启用前请确保服务器安装X-Sendfile模块并在站点配置文件中启用。</div>
</div>
<div class="row form-setting" style="{eq name="optionsForSet.sendfile" value="0"}display:none{/eq}" id="sendfile_header">
<div class="col-md-1 form-label ">
<label for="gravatar_server" class="col-form-label col-form-label-sm">Sendfile Header</label>
</div>
<div class="col-md-4">
<select class="form-control" name="header">
<option value="X-Sendfile" {eq name="optionsForSet.header" value="X-Sendfile"}selected{/eq}>X-Sendfile [Apache,Lighttpd v1.5,Cherokee]</option>
<option value="X-LIGHTTPD-send-file" {eq name="optionsForSet.header" value="X-LIGHTTPD-send-file"}selected{/eq}>X-LIGHTTPD-send-file [Lighttpd v1.4]</option>
<option value="X-Accel-Redirect" {eq name="optionsForSet.header" value="X-Accel-Redirect"}selected{/eq}>X-Accel-Redirect [Nginx,Cherokee]</option>
</select>
</div>
<div class="col-md-4 option-des"> 请根据你所使用的服务器软件选择合适的header</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
@@ -42,6 +42,12 @@
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="s3">S3</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="remote">远程</a>
</li>
<li class="nav-item">
<a class="nav-link selectLab" href="javascript:void()" data-policy="onedrive">Onedrive</a>
</li>
</ul>
</div>
</div>
@@ -78,6 +84,8 @@
{case value="oss"}OSS{/case}
{case value="upyun"}又拍云{/case}
{case value="s3"}Amazon S3{/case}
{case value="remote"}远程{/case}
{case value="onedrive"}Onedrive{/case}
{default /}其他
{/switch}
</td>
+99
View File
@@ -0,0 +1,99 @@
{extend name="header_admin" /}
{block name="title"}任务队列 - {$options.siteName}{/block}
{block name="content"}
<div class="content-wrapper">
<div class="container-fluid">
<!-- Breadcrumbs-->
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/Admin">管理面板</a>
</li>
<li class="breadcrumb-item active">任务队列</li>
<li class="breadcrumb-item active">配置</li>
</ol>
<!-- Area Chart Example-->
<div class="row">
<div class="col-12">
<h2>任务队列</h2>
<br>
<div class="card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#options"><i class="fa fa-cog" aria-hidden="true"></i> 配置</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list"><i class="fa fa-list" aria-hidden="true"></i> 任务列表</a>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content" >
<div class="tab-pane fade show active" id="options" role="tabpanel" aria-labelledby="pills-home-tab">
<form id="taskOptions">
<div class="row form-setting">
<div class="col-md-1 form-label ">
<label for="fromName" class="col-form-label col-form-label-sm">Token</label>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="task_queue_token" id="task_queue_token" value="{$taskOption}" spellcheck="false">
</div>
<div class="col-md-4 option-des"> 任务队列鉴权Token,应该与任务队列配置文件conf.yaml中的token保持一致,留空表示禁止任务队列请求</div>
</div>
<div class="row form-setting">
<div class="col-md-1 form-label ">
</div>
<div class="col-md-4"> <button type="button" class="btn btn-outline-secondary" id="generateToken">随机生成Token</button> <button type="button" class="btn btn-primary" id="saveTask">保存设置</button></div>
<div class="col-md-4 option-des"> </div>
<br><br><br>
</div>
</form>
</div>
<div class="tab-pane fade" id="list" role="tabpanel" aria-labelledby="pills-profile-tab">
<table class="table">
<thead>
<tr>
<th scope="col" class="textCenter">#</th>
<th scope="col" width="50%">任务名</th>
<th scope="col" class="textCenter">状态</th>
<th scope="col" class="textCenter">创建日期</th>
<th scope="col" class="textCenter">创建者UID</th>
</tr>
</thead>
<tbody id="listContent">
{volist name='task' id='t'}
<tr id="i-{$t.id}" >
<th scope="row" class="textCenter">{$t.id}</th>
<td>{$t.task_name}</td>
<td class="textCenter">{$t.status}</td>
<td class="textCenter">{$t.addtime}</td>
<td class="textCenter">{$t.uid}</td>
</tr>
{/volist}
</tbody>
</table>
{$task->render()}
</div>
<div class="tab-pane fade" id="tools" role="tabpanel" aria-labelledby="pills-profile-tab">
ddd
</div>
</div>
</div>
</div><br>
</div>
</div>
<!-- Example DataTables Card-->
</div>
<!-- /.container-fluid-->
</div>
{/block}
{block name="js"}
<script src="/static/js/admin/summernote.min.js"></script>
<script src="/static/js/admin/summernote-zh-CN.min.js"></script>
<script src="/static/js/admin/task.js"></script>
{/block}
-5
View File
@@ -1,6 +1,3 @@
{extend name="header_public" /}
{block name="title"}错误提示 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/error.css" />
</head>
<body data-ma-header="teal">
@@ -19,6 +16,4 @@
</div>
</div>
</div>
{$options.js_code}
</body>
{/block}
+23 -76
View File
@@ -1,76 +1,23 @@
{extend name="header_public" /}
{block name="title"}“{$keyWords}”搜索结果 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/search.css" />
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main-h">
<div class="col-md-12">
<h1>搜索分享</h1>
<input type="text" placeholder="请输入关键词,回车键发起搜索" value="{$keyWords}" autofocus="true"><br><br>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th width="50%">文件名 </th>
<th class="cent">分享日期</th>
<th class="cent">下载次数</th>
<th class="cent">浏览次数</th>
</tr>
</thead>
<tbody>{volist name='list' id='shares'}
<tr>
<td >{switch $shares.source_type}
{case file}<i class="fa fa-file" aria-hidden="true"></i> {/case}
{case dir}<i class="fa fa-folder-open blue" aria-hidden="true"></i> {/case}
{/switch} <a href="/s/{$shares.share_key}" class="notWave" target="_blank">{$shares.fileData|htmlspecialchars=ENT_NOQUOTES}</a></td>
<td class="cent">{$shares.share_time}</td>
<td align="center">{$shares.download_num}</td>
<td align="center">{$shares.view_num}</td>
</tr>
{/volist}
</tbody>
</table>
</div>
{$listOrigin->render()}
</div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/material.js"></script>
<script >
$.material.init();
document.onkeydown=function(){
if (event.keyCode == 13){
keyWords = $("input").val();
if(keyWords ==""){
toastr["warning"]("关键字不能为空");
}else{
window.location="/Explore/S/"+keyWords;
}
}
else{
}
}
</script>
{$options.js_code}
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>“{$keyWords}”搜索结果 - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="shareLock";
list = {$list};
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={21:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/2.chunk.js"></script><script src="/static/js/search.chunk.js"></script></body></html>
@@ -1,47 +0,0 @@
{extend name="header_public" /}
{block name="title"}用户主页 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/search.css" />
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main-h">
<div class="col-md-12">
<h1>搜索分享</h1>
<input type="text" placeholder="请输入关键词,回车键发起搜索" autofocus="true">
</div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/material.js"></script>
<script >
document.onkeydown=function(){
if (event.keyCode == 13){
keyWords = $("input").val();
if(keyWords ==""){
toastr["warning"]("关键字不能为空");
}else{
window.location="/Explore/S/"+keyWords;
}
}
else{
}
}
</script>
{$options.js_code}
{/block}
+1
View File
@@ -6,6 +6,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="keywords" content="{$options.siteKeywords}"/>
<meta name="description" content="{$options.siteDes}"/>
<meta name="theme-color" content="#4e64d9"/>
<title>{$options.siteName} - {$options.siteTitle}</title>
<!-- Font Awesome -->
+19 -2
View File
@@ -58,11 +58,19 @@
</li>
</ul>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="模板">
<a class="nav-link" href="/Admin/Theme">
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="模板">
<a class="nav-link nav-link-collapse collapsed" data-toggle="collapse" href="#theme">
<i class="fa fa-fw fa-paint-brush"></i>
<span class="nav-link-text">模板</span>
</a>
<ul class="sidenav-second-level collapse" id="theme">
<li>
<a href="/Admin/Theme">模板</a>
</li>
<li>
<a href="/Admin/Color">配色方案</a>
</li>
</ul>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="文件">
<a class="nav-link" href="/Admin/Files">
@@ -76,6 +84,12 @@
<span class="nav-link-text">分享</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="离线下载">
<a class="nav-link" href="/Admin/RemoteDownload">
<i class="fa fa-fw fa-cloud-download"></i>
<span class="nav-link-text">离线下载</span>
</a>
</li>
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="用户">
<a class="nav-link" href="/Admin/Users" data-parent="#user">
<i class="fa fa-fw fa-user"></i>
@@ -116,6 +130,9 @@
<span class="nav-link-text">其他</span>
</a>
<ul class="sidenav-second-level collapse" id="else">
<li>
<a href="/Admin/Queue">任务队列</a>
</li>
<li>
<a href="/Admin/Cron">定时任务</a>
</li>
+2 -11
View File
@@ -1,27 +1,18 @@
<!doctype html>
<html lang="zh-cn" data-ng-app="FileManagerApp">
<head>
<!--
* Angular FileManager v1.5.1 (https://github.com/joni2back/angular-filemanager)
* Jonas Sciangula Street <joni2back@gmail.com>
* Licensed under MIT (https://github.com/joni2back/angular-filemanager/blob/master/LICENSE)
-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>{block name="title"}{/block}</title>
<!-- third party -->
<meta name="theme-color" content="#4e64d9"/>
<title>{block name="title"}{/block}</title>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<!-- /third party -->
<!-- Comment if you need to use raw source code -->
<link href="/static/css/angular-filemanager.min.css" rel="stylesheet">
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<!-- /Comment if you need to use raw source code -->
<script type="text/javascript">
</script>
{block name="content"}{/block}
@@ -2,6 +2,7 @@
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>{block name="title"}{/block}</title>
<!-- third party -->
<script src="/static/js/jquery.min.js"></script>
-82
View File
@@ -1,82 +0,0 @@
<!DOCTYPE HTML>
<!--
Multiverse by HTML5 UP
html5up.net | @ajlkn
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
<head>
<title>图片集 - {$options.siteName}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<!--[if lte IE 8]><script src="https://file.aoaoao.me/content/themes/material/pages/js/ie/html5shiv.js"></script><![endif]-->
<link rel="stylesheet" href="/static/css/main.css" />
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
<!--[if lte IE 9]><link rel="stylesheet" href="https://file.aoaoao.me/content/themes/material/pages/css/ie9.css" /><![endif]-->
<!--[if lte IE 8]><link rel="stylesheet" href="https://file.aoaoao.me/content/themes/material/pages/css/ie8.css" /><![endif]-->
<style type="text/css">
body{
font-family: "微软雅黑","黑体";
}
</style>
</head>
<body>
<!-- Wrapper -->
<div id="wrapper">
<!-- Header -->
<div id="main">
{volist name='list' id='pic'}
<article class="thumb">
<a href="/File/Preview?action=preview&path={$pic.dir|urlencode}/{$pic.orign_name|urlencode}" class="image"><img src="/File/Preview?action=preview&path={$pic.dir|urlencode}/{$pic.orign_name|urlencode}" alt="" /></a>
<h2>{$pic.orign_name}</h2>
</article>
{/volist}
</div>
<header id="header">
<h1><a href="/Home"><strong>图片集</strong> {$options.siteName}</a></h1>
<nav>
<ul>
<li><a href="javascript:previous();" class="icon fa-arrow-circle-left">上一页</a></li>
{$page}/{$pageCount} <li><a href="javascript:next();" class="icon fa-arrow-circle-right">下一页</a></li>
</ul>
</nav>
</header>
<!-- Main -->
<!-- Footer -->
</div>
<!-- Scripts -->
<script src = "/static/js/jquery.min.js"></script>
<script src="/static/js/jquery.poptrox.min.js"></script>
<script src="/static/js/skel.min.js"></script>
<script src="/static/js/util.js"></script>
<!--[if lte IE 8]><script src="assets/js/ie/respond.min.js"></script><![endif]-->
<script src="/static/js/album.js"></script>
<script type="text/javascript">
pageNow = {$page};
pageTotal = {$pageCount};
function next(){
if(pageNow == pageTotal){
}else{
window.location.href="/Home/Album?page="+(pageNow+1);
}
}
function previous(){
if(pageNow == 1){
}else{
window.location.href="/Home/Album?page="+(pageNow-1);
}
}
</script>
{$options.js_code}
</body>
</html>
+22
View File
@@ -0,0 +1,22 @@
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>离线下载管理- {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="shareLock";
userInfo = {
uid: {$userInfo.uid},
nick: "{$userInfo.userNick}",
email: "{$userInfo.userMail}",
group: "{$userInfo.groupData.group_name}",
groupId: {$userInfo.groupData.id},
groupColor: "{$userInfo.groupData.color}",
};
siteInfo = {
mainTitle: "离线下载",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={20:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/2.chunk.js"></script><script src="/static/js/download.chunk.js"></script></body></html>
+43 -97
View File
@@ -1,97 +1,43 @@
<!doctype html>
<html lang="zh_cn" data-ng-app="FileManagerApp">
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>我的文件 - {$options.siteName}</title>
<script src="/static/js/angular.min.js"></script>
<script src="/static/js/angular-translate.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<link href="/static/css/angular-filemanager.min.css" rel="stylesheet">
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<script src="/static/js/angular-filemanager.min.js"></script>
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script type="text/javascript">
uploadConfig={
saveType : "{$policyData.policy_type}",
maxSize : "{$policyData.max_size}mb",
allowedType: [
{$extLimit}
],
allowSource : "{$policyData.origin_link}",
upUrl : "{$policyData.server}",
allowShare:"{$groupData.allow_share}",
};
</script>
<script src="/static/js/home.js"></script>
</head>
<body class="ng-cloak">
<div id="container">
{include file="navbar_home" /}
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<div class="pswp__container">
<!-- don't modify these 3 pswp__item elements, data is added later on -->
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<div class="col-md-10 max_height">
<angular-filemanager></angular-filemanager>
</div>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>我的文件 - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = true;
pageId="";
isSharePage = false;
uploadConfig = {
saveType: "{$policyData.policy_type}",
maxSize: "{$policyData.max_size}mb",
allowedType: [{$extLimit}],
allowSource: "{$policyData.origin_link}",
upUrl: "{$policyData.server}",
allowShare: "{$groupData.allow_share}",
allowRemoteDownload: "{:explode(",",$groupData.aria2)[0]}",
allowTorrentDownload: "{:explode(",",$groupData.aria2)[1]}",
};
if(uploadConfig.saveType == "oss" || uploadConfig.saveType == "upyun" || uploadConfig.saveType == "s3"){
ChunkSize = "0";
}else if(uploadConfig.saveType == "qiniu" ){
ChunkSize = 4*1024*1024;
}else{
ChunkSize = {$chunkSize};
}
apiURL = {
imgThumb: "/File/Thumb",
preview: "/File/Preview",
download: "/File/Download",
docPreiview: '/File/DocPreview',
listFile:"/File/ListFile"
};
userInfo = {
uid: {$userInfo.uid},
nick: "{$userInfo.userNick}",
email: "{$userInfo.userMail}",
group: "{$userInfo.groupData.group_name}",
groupId: {$userInfo.groupData.id},
groupColor: "{$userInfo.groupData.color}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
pathCache = {};
path = "{$path}";
mediaType = {},
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(c){function e(e){for(var t,r,n=e[0],o=e[1],a=e[2],u=0,i=[];u<n.length;u++)r=n[u],f[r]&&i.push(f[r][0]),f[r]=0;for(t in o)Object.prototype.hasOwnProperty.call(o,t)&&(c[t]=o[t]);for(d&&d(e);i.length;)i.shift()();return s.push.apply(s,a||[]),l()}function l(){for(var e,t=0;t<s.length;t++){for(var r=s[t],n=!0,o=1;o<r.length;o++){var a=r[o];0!==f[a]&&(n=!1)}n&&(s.splice(t--,1),e=p(p.s=r[0]))}return e}var r={},i={19:0},f={19:0},s=[];function p(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return c[e].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.e=function(s){var e=[];i[s]?e.push(i[s]):0!==i[s]&&{15:1}[s]&&e.push(i[s]=new Promise(function(e,n){for(var t="static/css/"+({}[s]||s)+".chunk.css",o=p.p+t,r=document.getElementsByTagName("link"),a=0;a<r.length;a++){var u=(c=r[a]).getAttribute("data-href")||c.getAttribute("href");if("stylesheet"===c.rel&&(u===t||u===o))return e()}var i=document.getElementsByTagName("style");for(a=0;a<i.length;a++){var c;if((u=(c=i[a]).getAttribute("data-href"))===t||u===o)return e()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=e,l.onerror=function(e){var t=e&&e.target&&e.target.src||o,r=new Error("Loading CSS chunk "+s+" failed.\n("+t+")");r.request=t,n(r)},l.href=o,document.getElementsByTagName("head")[0].appendChild(l)}).then(function(){i[s]=0}));var r=f[s];if(0!==r)if(r)e.push(r[2]);else{var t=new Promise(function(e,t){r=f[s]=[e,t]});e.push(r[2]=t);var n,o=document.getElementsByTagName("head")[0],a=document.createElement("script");a.charset="utf-8",a.timeout=120,p.nc&&a.setAttribute("nonce",p.nc),a.src=p.p+"static/js/"+({}[s]||s)+".chunk.js",n=function(e){a.onerror=a.onload=null,clearTimeout(u);var t=f[s];if(0!==t){if(t){var r=e&&("load"===e.type?"missing":e.type),n=e&&e.target&&e.target.src,o=new Error("Loading chunk "+s+" failed.\n("+r+": "+n+")");o.type=r,o.request=n,t[1](o)}f[s]=void 0}};var u=setTimeout(function(){n({type:"timeout",target:a})},12e4);a.onerror=a.onload=n,o.appendChild(a)}return Promise.all(e)},p.m=c,p.c=r,p.d=function(e,t,r){p.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(t,e){if(1&e&&(t=p(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(p.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)p.d(r,n,function(e){return t[e]}.bind(null,n));return r},p.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(t,"a",t),t},p.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},p.p="/",p.oe=function(e){throw console.error(e),e};var t=window.webpackJsonp=window.webpackJsonp||[],n=t.push.bind(t);t.push=e,t=t.slice();for(var o=0;o<t.length;o++)e(t[o]);var d=n;l()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/index.chunk.js"></script></body></html>
+3 -2
View File
@@ -3,7 +3,7 @@
<!--Video-->
<div class="intro-video view" id="home">
<img src="static/img/output.jpg">
<div class="hm-gradient">
@@ -16,7 +16,8 @@
<hr class="hr-light">
<h4 class="subtext-header mt-2 mb-2">只需十秒钟,注册后即可开始使用免费高速的云存储服务</h4>
</div>
<a href="/Login" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-paper-plane-o"></i> 立即注册</a>
<a href="/Login" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-paper-plane-o"></i> 注册</a>
<a href="/Home" class="btn btn-rounded btn-light-green-2" ><i class="fa fa-sign-in"></i> 登录</a>
</div>
</div>
</div>
@@ -1,59 +0,0 @@
{extend name="header_public" /}
{block name="title"}注册成功- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/login.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-check" aria-hidden="true"></i></div>
<div class="login-text">注册成功</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<br>
您已成功激活账号。<br><br>
<a class="btn btn-raised btn-primary" href="/Login">立即登录</a>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/two_step.js"> </script>
{$options.js_code}
{/block}
+23 -185
View File
@@ -1,185 +1,23 @@
{extend name="header_public" /}
{block name="title"}登录- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/login.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" id="logForm">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-user-circle-o" aria-hidden="true"></i></div>
<div class="login-text">登录后方可享用精彩内容</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="loginForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">Email 电子邮箱</label>
<input type="text" class="form-control" id="inputEmail" name="userMail">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">密码</label>
<input type="password" class="form-control" id="inputPwd" name="userPass">
</div>
{eq name="$RegOptions.login_captcha" value="1"}
<div class="input-group form-group label-floating">
<label class="control-label" for="captcha">验证码</label>
<input type="text" id="captcha" class="form-control" name="captchaCode">
<span class="input-group-btn">
<div class="captcha_img">{:captcha_img()}</div>
</span>
</div>
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="loginButton">登录</button>
<div class="link-group">
<a href="javascript:void" class="noWave" id="create" data-change="true">创建一个账户</a><br>
<a href="javascript:void" class="noWave" id="forgetSwitch2" data-change="true">忘记密码?</a>
</div>
</div>
</div>
</div>
<div class="jumbotron" id="regForm" style="display: none;">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-sign-in" aria-hidden="true"></i></div>
<div class="login-text">注册账户</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="registerForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">Email 电子邮箱</label>
<input type="text" class="form-control" id="inputEmail" name="username-reg">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">密码</label>
<input type="password" class="form-control" id="inputPwd" name="password-reg">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">重复密码</label>
<input type="password" class="form-control" id="inputPwd" name="password-check">
</div>
{eq name="$RegOptions.reg_captcha" value="1"}
<div class="input-group form-group label-floating">
<label class="control-label" for="captcha">验证码</label>
<input type="text" id="captcha" class="form-control" name="captchaCode">
<span class="input-group-btn">
<div class="captcha_img">{:captcha_img()}</div>
</span>
</div>
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="regButton">注册</button> <div class="link-group">
<a href="javascript:void" class="noWave" id="loginSwitch3" data-change="true">已有账号?立即登录</a><br>
<a href="javascript:void" class="noWave" id="forgetSwitch" data-change="true">忘记密码?</a>
</div>
</div>
</div>
</div>
<div class="jumbotron" id="emailCheck" style="display: none;">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-envelope" aria-hidden="true"></i></div>
<div class="login-text">激活账户</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width"><br>
我们已经向您的注册邮箱发送了一封确认邮件,请访问邮件中的激活链接以完成注册。如果未收到邮件,请检查您的垃圾邮件。<br><br>如果还仍无法接受邮件,请尝试更换注册邮箱或联系站点管理员。
</div>
</div>
</div>
<div class="jumbotron" id="forgetForm" style="display: none;">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-question-circle-o" aria-hidden="true"></i></div>
<div class="login-text">找回密码</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="forgetPwdForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">请填写注册时绑定的邮箱</label>
<input type="text" class="form-control" id="regEmail" name="regEmail">
</div>
{eq name="$RegOptions.forget_captcha" value="1"}
<div class="input-group form-group label-floating">
<label class="control-label" for="captcha">验证码</label>
<input type="text" id="captcha" class="form-control" name="captchaCode">
<span class="input-group-btn">
<div class="captcha_img">{:captcha_img()}</div>
</span>
</div>
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="findMyFuckingPwd">找回密码</button> <div class="link-group">
<a href="javascript:void" class="noWave" id="loginSwitch2" data-change="true">返回登录</a><br>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/login.js"> </script>
{$options.js_code}
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>{eq name="pageId" value="resetPwdForm"}找回密码{/eq}{eq name="pageId" value="resetPwd"}找回密码{/eq}{eq name="pageId" value="emailActivate"}激活成功{/eq}{eq name="pageId" value="login"}登录{/eq}{eq name="pageId" value="register"}注册{/eq}{eq name="pageId" value="TwoStep"}二步验证{/eq} - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
captcha = "{$RegOptions.login_captcha}";
regCaptcha = "{$RegOptions.reg_captcha}";
findPwdCaptcha = "{$RegOptions.forget_captcha}";
{if isset($key)}
resetKey = '{$key}';
{/if}
pageId="{$pageId}";
userInfo = {
uid: -1,
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={22:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/login.chunk.js"></script></body></html>
@@ -1,122 +0,0 @@
{extend name="header_public" /}
{block name="title"}登录- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/login.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" id="logForm">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-user-circle-o" aria-hidden="true"></i></div>
<div class="login-text">登录后方可享用精彩内容</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="loginForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">Email 电子邮箱</label>
<input type="text" class="form-control" id="inputEmail" name="userMail">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">密码</label>
<input type="password" class="form-control" id="inputPwd" name="userPass">
</div>
{eq name="$RegOptions.login_captcha" value="1"}
<div class="input-group form-group label-floating">
<label class="control-label" for="captcha">验证码</label>
<input type="text" id="captcha" class="form-control" name="captchaCode">
<span class="input-group-btn">
<div class="captcha_img">{:captcha_img()}</div>
</span>
</div>
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="loginButton">登录</button> <button class="btn btn-raised btn-info" ><i class="fa fa-qq" aria-hidden="true"></i> 使用QQ账号登录</button><div class="link-group">
<a href="#" class="noWave">创建一个账户</a><br>
<a href="#" class="noWave">忘记密码?</a>
</div>
</div>
</div>
</div>
<div class="jumbotron" id="regForm">
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-user-circle-o" aria-hidden="true"></i></div>
<div class="login-text">登录后方可享用精彩内容</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="loginForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">Email 电子邮箱</label>
<input type="text" class="form-control" id="inputEmail" name="userMail">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">密码</label>
<input type="password" class="form-control" id="inputPwd" name="userPass">
</div>
{eq name="$RegOptions.login_captcha" value="1"}
<div class="input-group form-group label-floating">
<label class="control-label" for="captcha">验证码</label>
<input type="text" id="captcha" class="form-control" name="captchaCode">
<span class="input-group-btn">
<div class="captcha_img">{:captcha_img()}</div>
</span>
</div>
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="loginButton">登录</button> <button class="btn btn-raised btn-info" ><i class="fa fa-qq" aria-hidden="true"></i> 使用QQ账号登录</button><div class="link-group">
<a href="#" class="noWave">创建一个账户</a><br>
<a href="#" class="noWave">忘记密码?</a>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/login_bind.js"> </script>
{$options.js_code}
{/block}
@@ -1,70 +0,0 @@
{extend name="header_public" /}
{block name="title"}密码重置- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/login.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-id-card-o" aria-hidden="true"></i></div>
<div class="login-text">密码重置</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="resetForm">
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">请输入新密码</label>
<input type="password" class="form-control" id="inputPwd" name="password-reg">
</div>
<div class="form-group label-floating">
<label class="control-label" for="inputPwd">重复密码</label>
<input type="password" class="form-control" id="inputPwd" name="password-check">
</div>
<input type="text" style="display:none" class="form-control" id="resetKey" name="key" value="{$key}">
</form> <button class="btn btn-raised btn-primary" id="regButton">重设密码</button>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/reset.js"> </script>
{$options.js_code}
{/block}
+28 -265
View File
@@ -1,265 +1,28 @@
{extend name="header_home" /}
{block name="title"}用户设置- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/setting.css" />
<style type="text/css">
.col-md-3{
padding-right: 15px;
padding-left: 15px;
}
</style>
</head>
<body >
<div id="container">
{include file="navbar_home" /}
<div class="col-md-10 quota_content">
<h1>用户设置</h1>
<br>
<div class="fix_side">
<div class="fix">
<div class="col-md-9">
<ul class="nav nav-tabs" >
<li class="active"><a href="#home" data-toggle="tab" aria-expanded="true">基本信息<div class="ripple-container"></div></a></li>
<li class=""><a href="#security" data-toggle="tab" aria-expanded="false">安全隐私<div class="ripple-container"></div></a></li>
<li class=""><a href="#password" data-toggle="tab" aria-expanded="false">修改密码<div class="ripple-container"></div></a></li>
<li class=""><a href="#webdav" data-toggle="tab" aria-expanded="false">WebDAV<div class="ripple-container"></div></a></li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="home">
<div class="panel panel-default">
<div class="panel-body" id="packs">
<div class="col-md-8">
<div class="row fix">
<div class="col-md-3 option_name"><label for="uid">UID</label></div>
<div class="col-md-9">
<div class="non_input">{$userInfo.uid}</div>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="nick">昵称:</label></div>
<div class="col-md-6">
<input type="text" class="form-control" id="nick" value="{$userInfo.userNick}" >
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="email">Email</label></div>
<div class="col-md-9">
<input type="email" class="form-control" id="email" value="{$userInfo.userMail}" disabled>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">用户组:</label></div>
<div class="col-md-9">
<div class="non_input group">{$groupData.group_name} </div>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">注册日期:</label></div>
<div class="col-md-9">
<div class="non_input ">{$userInfo.regDate}</div>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail"></label></div>
<div class="col-md-9">
<div class="non_input "><button class="btn btn-raised btn-primary" id="saveNick">保存更改</button></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="avatar">
<img src="/Member/Avatar/{$userInfo.uid}/l?cache=no" class="img-circle avatar-img"><br>
<button class="btn btn-primary" data-toggle="modal" data-target="#avatar_modal"><i class="fa fa-pencil-square-o" aria-hidden="true"></i> 修改头像</button>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="security">
<div class="panel panel-default">
<div class="panel-body" >
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">个人主页:</label></div>
<div class="col-md-9">
<div ><div class="togglebutton">
<label>
<input type="checkbox" id="homePage" {eq name="$userSQL.profile" value="1"}checked{else}{/eq}>
</label>
</div></div>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name" style="margin-top: 10px;"><label for="inputEmail">二步验证:</label></div>
<div class="col-md-9">
{eq name="$userSQL.two_step" value="0"}
<button class="btn btn-primary btn-raised" id="twoStep"><i class="fa fa-lock" aria-hidden="true"></i> 开启二步验证</button>
{else}
<button class="btn btn-primary btn-raised"><i class="fa fa-check" aria-hidden="true"></i> 已开启</button>
{/eq}
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="password">
<div class="panel panel-default">
<div class="panel-body" >
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">原密码:</label></div>
<div class="col-md-6">
<input type="password" class="form-control" id="passOrigin" >
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">新密码:</label></div>
<div class="col-md-6">
<input type="password" class="form-control" id="passNew" >
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">确认新密码:</label></div>
<div class="col-md-6">
<input type="password" class="form-control" id="passNewRepet" >
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail"></label></div>
<div class="col-md-9">
<div class="non_input "><button class="btn btn-raised btn-primary waves-effect" id="savePwd">保存更改</button></div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="webdav">
<div class="panel panel-default">
<div class="panel-body" >
{eq name="groupData.webdav" value="0"}
<div class="alert alert-warning" role="alert">
您当前的用户组不支持WebDAV
</div>
{else/}
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">连接地址:</label></div>
<div class="col-md-6">
<input type="text" class="form-control" id="webdavUrl" value="{$options.siteURL}WebDav/Api/uid/{$userInfo.uid}" spellcheck="false" readonly>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name"><label for="inputEmail">用户名:</label></div>
<div class="col-md-6">
<input type="text" class="form-control" id="webdavUsername" value="{$userInfo.userMail}" spellcheck="false" readonly>
</div>
</div>
<div class="row fix">
<div class="col-md-3 option_name" style="margin-top: 10px;"><label for="inputEmail">登录密码:</label></div>
<div class="col-md-9">
<button class="btn btn-primary btn-raised" id="setWebdavPwd"><i class="fa fa-lock" aria-hidden="true"></i> 设置/更改密码</button>
</div>
</div>
{/eq}<br>
<p>你可以使用任何支持WebDAV协议的文件管理工具或操作系统将网盘映射到本地,方便多端同步管理。初次使用请先设置WebDAV登录密码,此密码默认与登录密码不相同。</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3"><div class="panel panel-default">
<div class="panel-heading">公告</div>
<div class="panel-body">
公告内容
</div>
</div></div>
</div>
</div>
<br>
<div class="fix_side">
<div class="fix">
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="avatar_modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">修改头像</h4>
</div>
<div class="modal-body">
<div class="row fix">
<div class="col-md-4"><div class="avatar"><img src="/Member/Avatar/{$userInfo.uid}/l?cache=no" class="img-circle avatar-img"><br></div></div>
<div class="col-md-8">
<span class="btn btn-raised btn-info fileinput-button" id="uploadAvatar">
<span id="upload-text">上传头像</span>
<input type="file" accept="image/*" id="avatar_file" name="avatar">
</span>
<br><button class="btn btn-raised btn-info" id="useGravatar">使用Gravatar头像</button>
<br>修改头像后请清理浏览器缓存
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary " data-dismiss="modal">取消</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade" tabindex="-1" role="dialog" id="two_step_modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">开启二步验证</h4>
</div>
<div class="modal-body">
<div class="row fix">
<div class="col-md-4"><img id="qrcode"></div>
<div class="col-md-8">
<div class="alert alert-success" role="alert">请使用任意二步验证APP或者支持二步验证的密码管理软件扫描左侧二维码添加本站。扫描完成后请填写二步验证APP给出的6位验证码以开启二步验证。</div>
<input type="number" class="form-control" placeholder="请输入6位验证码" id="vCode">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-info " id="confirm">确认开启</button>
<button class="btn btn-primary " data-dismiss="modal">取消</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade" tabindex="-1" role="dialog" id="set_webdav_pwd">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">设置WebDAV认证密码</h4>
</div>
<div class="modal-body">
<div class="row fix">
<label>请输入密码:</label>
<input type="password" id="webdav_pwd" class="form-control" autocomplete="false">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-info " id="confirmWebdav">保存</button>
<button class="btn btn-primary " data-dismiss="modal">取消</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body>
<script src="/static/js/material.js"></script>
<script src="/static/js/uploader.min.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
<script src="/static/js/setting.js"></script>
{$options.js_code}
</html>
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>用户设置- {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
colorThemeOptions = {$options["themes"]};
isHomePage = false;
isSharePage = false;
pageId="userSetting";
siteUrl = "{$options.siteURL}";
userInfo = {
uid: {$userInfo.uid},
nick: "{$userInfo.userNick}",
email: "{$userInfo.userMail}",
group: "{$userInfo.groupData.group_name}",
groupId: {$userInfo.groupData.id},
groupColor: "{$userInfo.groupData.color}",
regTime: "{$userSQL.user_date}",
homePage: "{$userSQL.profile}",
twoFactor: "{$userSQL.two_step}",
webdav:"{$userInfo.groupData.webdav}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={26:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/setting.chunk.js"></script></body></html>
@@ -1,66 +0,0 @@
{extend name="header_public" /}
{block name="title"}两步验证- {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/login.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="login-icon"><i class="fa fa-lock" aria-hidden="true"></i></div>
<div class="login-text">由于您开启了两步验证,请输入您的两步验证APP上显示的验证码</div>
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<form id="loginForm">
<div class="form-group label-floating">
<label class="control-label" for="inputEmail">验证码</label>
<input type="text" class="form-control" id="inputEmail" name="code">
</div>
</form> <button class="btn btn-raised btn-primary" id="loginButton">继续登录</button>
<br>如果您丢失了两步验证秘钥,请联系站点管理员核验身份后取消二步验证。
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/two_step.js"> </script>
{$options.js_code}
{/block}
+21 -4
View File
@@ -2,7 +2,7 @@
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<a class="navbar-brand" href="/">
<a class="navbar-brand waves-light" href="/">
</a>
@@ -19,14 +19,26 @@
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle avatar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userInfo.uid}/s" class="img-circle avatar-s"> {$userInfo.userNick} <span class="caret"></span></a>
<a href="#" class="dropdown-toggle avatar-a waves-light" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userInfo.uid}/s" class="img-circle avatar-s"> {$userInfo.userNick} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/Profile/{$userInfo.uid}">个人主页</a></li>
<li><a href="/Member/Setting">设置</a></li>
<li role="separator" class="divider"></li>
{eq name="$userInfo.groupId" value="1"}
<li><a href="/Admin">管理面板</a></li>
{/eq}
<li role="separator" class="divider"></li>
<li><a href="/Member/LogOut">退出登录</a></li>
</ul>
</li>
<li class="mobile-addition">
<a href="/Share/My" role="button" aria-haspopup="true" ><i class="fa fa-share-alt" aria-hidden="true"></i> 我的分享</a>
</li>
<li class="mobile-addition">
<a href="/Explore/Search" role="button" aria-haspopup="true" ><i class="fa fa-search" aria-hidden="true"></i> 搜索分享</a>
</li>
<li class="mobile-addition">
<a href="/Home/Album" role="button" aria-haspopup="true" ><i class="fa fa-picture-o" aria-hidden="true"></i> 图片集</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
@@ -51,7 +63,7 @@
<div id="container">
<button class="btn btn-raised btn-info btn-lg upload_button" id="pickfiles" >
<button class="btn btn-raised btn-info btn-lg upload_button waves-light" id="pickfiles" >
<i class="glyphicon glyphicon-plus"></i>
<span id="up_text"></span>
</button>
@@ -117,6 +129,11 @@
<a href="/Home/Album" class="list-group-item">
<i class="fa fa-picture-o" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 图片集
</a>
{eq name=':(explode(",",$groupData.aria2)[0] OR explode(",",$groupData.aria2)[1])' value="1"}
<a href="/Home/Download" class="list-group-item">
<i class="fa fa-cloud-download" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 离线下载
</a>
{/eq}
</div>
<div clss="usage" style=" visibility: visible;
position: absolute;
+3 -3
View File
@@ -1,6 +1,6 @@
<div class="navbar-header">
<div >
<a class="navbar-brand" href="/">
<a class="navbar-brand waves-light" href="/">
</a>
</div>
@@ -19,7 +19,7 @@
<ul class="nav navbar-nav navbar-right">
{eq name="$loginStatus" value="1"}
<li class="dropdown">
<a href="#" class="dropdown-toggle avatar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userData.id}/s" class="img-circle avatar-s"> {$userData.user_nick} <span class="caret"></span></a>
<a href="#" class="dropdown-toggle avatar-a waves-light" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><img src="/Member/Avatar/{$userData.id}/s" class="img-circle avatar-s"> {$userData.user_nick} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/Home">我的文件</a></li>
<li><a href="/Profile/{$userData.id}">个人主页</a></li>
@@ -30,7 +30,7 @@
</li>
{else/}
<li >
<a href="/Login" class="dropdown-toggle" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-user mr-1"></i> 登录/注册 </a>
<a href="/Login" class="dropdown-toggle waves-light" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-user mr-1"></i> 登录/注册 </a>
</li>
{/eq}
+33 -115
View File
@@ -1,115 +1,33 @@
{extend name="header_public" /}
{block name="title"}用户主页 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/profile.css" />
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main-h">
<div class="col-md-3">
<div class="card type--profile">
<header class="card-heading card-background" id="card_img_02">
<img src="/Member/Avatar/{$userInfo.id}/l" alt="" class="img-circle">
<ul class="card-actions icons right-top">
<li class="dropdown" style="display: none;">
<a href="javascript:void(0)" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-more-vert text-white"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a href="javascript:void(0)">Option One</a>
</li>
<li>
<a href="javascript:void(0)">Option Two</a>
</li>
<li>
<a href="javascript:void(0)">Option Three</a>
</li>
</ul>
</li>
</ul>
</header>
<div class="card-body">
<h3 class="name">{$userInfo.user_nick|htmlspecialchars=ENT_NOQUOTES}</h3>
<span class="title span-fix"><span class="label span-fix label-{$groupData.color}">{$groupData.group_name}</span></span>
</div>
<footer class="card-footer border-top">
<div class="row row p-t-10 p-b-10">
<div class="col-xs-4"><span class="count">{$shareCount}</span><span>公开分享</span></div>
<div class="col-xs-4"><span class="count">{$regDays}</span><span>注册天数</span></div>
<div class="col-xs-4"><span class="count">{$userInfo.id}</span><span>用户编号</span></div>
</div>
</footer>
</div>
</div>
<div class="col-md-9">
<div class="jumbotron">
<div class="card_botom">
<div class="row bottom-width">
<ul class="nav nav-tabs" >
<li id="all"><a href="?page=1" aria-expanded="true">全部分享<div class="ripple-container"></div></a></li>
<li id="hot"><a href="?type=hot" aria-expanded="false">热门分享<div class="ripple-container"></div></a></li>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in">
<div class="panel panel-default">
<div class="panel-body" >
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th width="50%">文件名 </th>
<th class="cent">分享日期</th>
<th class="cent">下载次数</th>
<th class="cent">浏览次数</th>
</tr>
</thead>
<tbody>{volist name='list' id='shares'}
<tr>
<td >{switch $shares.source_type}
{case file}<i class="fa fa-file" aria-hidden="true"></i> {/case}
{case dir}<i class="fa fa-folder-open blue" aria-hidden="true"></i> {/case}
{/switch} <a href="/s/{$shares.share_key}" class="fname" target="_blank">{$shares.fileData|htmlspecialchars=ENT_NOQUOTES}</a></td>
<td class="cent">{$shares.share_time}</td>
<td align="center">{$shares.download_num}</td>
<td align="center">{$shares.view_num}</td>
</tr>
{/volist}
</tbody>
</table>
</div>
<div class="navi">
{eq name="$type" value="hot"}
{else/}
{$listOrigin->render()}
{/eq}
</div>
</div></div></div></div>
</div>
</div>
</div></div>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script src="/static/js/material.js"></script>
<script src="/static/js/profile.js"> </script>
{$options.js_code}
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>用户主页 - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="profile";
taegetUserInfo = {
uid:{$targetUserInfo.id},
nickname:"{$targetUserInfo.user_nick}",
regDate:"{$targetUserInfo.user_date}",
shareCount:"{$shareCount}",
group:"{$groupData.group_name}",
};
userInfo = {
uid: {$userInfo.uid},
nick: "{$userInfo.userNick}",
email: "{$userInfo.userMail}",
group: "{$userInfo.groupData.group_name}",
groupId: {$userInfo.groupData.id},
groupColor: "{$userInfo.groupData.color}",
regTime: "{$userSQL.user_date}",
homePage: "{$userSQL.profile}",
twoFactor: "{$userSQL.two_step}",
webdav:"{$userInfo.groupData.webdav}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={25:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/16.chunk.js"></script><script src="/static/js/profile.chunk.js"></script></body></html>
-83
View File
@@ -1,83 +0,0 @@
<!doctype html>
<html lang="zh_cn" data-ng-app="FileManagerApp">
<head>
<!--
* Angular FileManager v1.5.1 (https://github.com/joni2back/angular-filemanager)
* Jonas Sciangula Street <joni2back@gmail.com>
* Licensed under MIT (https://github.com/joni2back/angular-filemanager/blob/master/LICENSE)
-->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>我的文件 - {$options.siteName}</title>
<!-- third party -->
<script src="/static/js/angular.min.js"></script>
<script src="/static/js/angular-translate.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/material.css" />
<script src="/static/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.0/css/font-awesome.min.css">
<!-- /third party -->
<!-- Comment if you need to use raw source code -->
<link href="/static/css/angular-filemanager.min.css" rel="stylesheet">
<link href="/static/css/toastr.min.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<script src="/static/js/angular-filemanager.min.js"></script>
<!-- /Comment if you need to use raw source code -->
<script type="text/javascript">
uploadConfig={
saveType : "{$policyData.policy_type}",
maxSize : "{$policyData.max_size}mb",
allowedType: [
{$extLimit}
],
allowSource : "{$policyData.origin_link}",
upUrl : "{$policyData.server}",
allowShare:"{$groupData.allow_share}",
};
</script>
<script src="/static/js/home.js"></script>
</head>
<body class="ng-cloak">
<div id="container">
{include file="navbar_home" /}
<div class="col-md-2 s" id="side">
<div class="list-group" id="b">
<a href="/Home" class="list-group-item active">
<i class="fa fa-file" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 我的文件
</a>
<a href="/Share" class="list-group-item"> <i class="fa fa-share-alt" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 我的分享
</a>
<a href="#" class="list-group-item"><i class="fa fa-database" aria-hidden="true"></i>&nbsp;&nbsp;&nbsp; 容量配额
</a>
</div>
<div clss="usage" style=" visibility: visible;
position: absolute;
width: 100%;
height: 100px;
top: auto;
bottom: 0px;
background-color: #f3f3f3;padding: 15px">
<div class="usage-title">容量使用:</div>
<div class="usage-bar">
<div class="progress progress-striped active">
<div class="progress-bar" id="memory_bar"></div>
</div>
</div>
<div class="usage-text"><span id="used">--</span>/<span id="total">--</span></div>
</div>
</div>
<div class="col-md-10">
<angular-filemanager></angular-filemanager>
</div>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
+31 -114
View File
@@ -1,105 +1,32 @@
{extend name="header_dir_share" /}
{block name="title"}{$dirData.folder_name} - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/share_dir.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
</head>
<body data-ma-header="teal">
<div class="modal fade" id="previewModal" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" onclick="audioPause()" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only ng-binding">关闭</span>
</button>
<h4 class="modal-title">视频预览</h4>
</div>
<div class="modal-body">
<div class="text-center previewContent">
</div>
</div>
</div>
</div>
</div>
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<div class="pswp__container">
<!-- don't modify these 3 pswp__item elements, data is added later on -->
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userInfo /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main-h">
<div class="">
<div class="jumbotron">
<div class="card_botom">
<div class="row bottom-width">
<div class="manager"><angular-filemanager></angular-filemanager>
</div>
</div>
</div>
</div></div>
</div>
</div>
</body>
<script type="text/javascript">
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>{$dirData.folder_name} - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = true;
pageId="";
apiURL = {
imgThumb: "/Share/Thumb/{$shareData.share_key}",
preview: "/Share/Preview/{$shareData.share_key}",
download: "/Share/Download/{$shareData.share_key}",
docPreiview: '/Share/DocPreview/{$shareData.share_key}',
listFile:'/Share/ListFile/{$shareData.share_key}'
};
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$dirData.folder_name}",
};
path = "{$path}";
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
shareInfo={
shareDate : "{$shareData.share_time}",
ownerUid:"{$userInfo.id}",
@@ -116,15 +43,5 @@
allowPreview:{$allowPreview},
{/eq}
};
</script>
<script src="/static/js/angular.min.js"></script>
<script src="/static/js/angular-translate.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/material.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/static/js/toastr.min.js"></script>
<script src="/static/js/filemanager_share.js"></script>
<script src="/static/js/share_dir.js"> </script>
{$options.js_code}
{/block}
mediaType = {},
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(c){function e(e){for(var t,r,n=e[0],o=e[1],a=e[2],u=0,i=[];u<n.length;u++)r=n[u],f[r]&&i.push(f[r][0]),f[r]=0;for(t in o)Object.prototype.hasOwnProperty.call(o,t)&&(c[t]=o[t]);for(d&&d(e);i.length;)i.shift()();return s.push.apply(s,a||[]),l()}function l(){for(var e,t=0;t<s.length;t++){for(var r=s[t],n=!0,o=1;o<r.length;o++){var a=r[o];0!==f[a]&&(n=!1)}n&&(s.splice(t--,1),e=p(p.s=r[0]))}return e}var r={},i={27:0},f={27:0},s=[];function p(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return c[e].call(t.exports,t,t.exports,p),t.l=!0,t.exports}p.e=function(s){var e=[];i[s]?e.push(i[s]):0!==i[s]&&{15:1}[s]&&e.push(i[s]=new Promise(function(e,n){for(var t="static/css/"+({}[s]||s)+".chunk.css",o=p.p+t,r=document.getElementsByTagName("link"),a=0;a<r.length;a++){var u=(c=r[a]).getAttribute("data-href")||c.getAttribute("href");if("stylesheet"===c.rel&&(u===t||u===o))return e()}var i=document.getElementsByTagName("style");for(a=0;a<i.length;a++){var c;if((u=(c=i[a]).getAttribute("data-href"))===t||u===o)return e()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=e,l.onerror=function(e){var t=e&&e.target&&e.target.src||o,r=new Error("Loading CSS chunk "+s+" failed.\n("+t+")");r.request=t,n(r)},l.href=o,document.getElementsByTagName("head")[0].appendChild(l)}).then(function(){i[s]=0}));var r=f[s];if(0!==r)if(r)e.push(r[2]);else{var t=new Promise(function(e,t){r=f[s]=[e,t]});e.push(r[2]=t);var n,o=document.getElementsByTagName("head")[0],a=document.createElement("script");a.charset="utf-8",a.timeout=120,p.nc&&a.setAttribute("nonce",p.nc),a.src=p.p+"static/js/"+({}[s]||s)+".chunk.js",n=function(e){a.onerror=a.onload=null,clearTimeout(u);var t=f[s];if(0!==t){if(t){var r=e&&("load"===e.type?"missing":e.type),n=e&&e.target&&e.target.src,o=new Error("Loading chunk "+s+" failed.\n("+r+": "+n+")");o.type=r,o.request=n,t[1](o)}f[s]=void 0}};var u=setTimeout(function(){n({type:"timeout",target:a})},12e4);a.onerror=a.onload=n,o.appendChild(a)}return Promise.all(e)},p.m=c,p.c=r,p.d=function(e,t,r){p.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},p.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},p.t=function(t,e){if(1&e&&(t=p(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(p.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)p.d(r,n,function(e){return t[e]}.bind(null,n));return r},p.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return p.d(t,"a",t),t},p.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},p.p="/",p.oe=function(e){throw console.error(e),e};var t=window.webpackJsonp=window.webpackJsonp||[],n=t.push.bind(t);t.push=e,t=t.slice();for(var o=0;o<t.length;o++)e(t[o]);var d=n;l()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/folderShare.chunk.js"></script></body></html>
+22 -98
View File
@@ -1,98 +1,22 @@
{extend name="header_home" /}
{block name="title"}我的分享- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/share_home.js"></script>
</head>
<body >
<div class="modal fade bs-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" id="deleteConfirm">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">确认</h4>
</div>
<div class="modal-body">
您确定要取消此分享吗?
</div>
<div class="modal-footer">
<button type="button" class="btn pro-btn" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary pro-btn" onclick="deleteConfirm();">确定</button>
</div>
</div>
</div>
</div>
<div class="modal fade bs-example-modal-sm" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" id="changeConfirm">
<div class="modal-dialog modal-sm" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">确认</h4>
</div>
<div class="modal-body">
您确定要将此分享改为<span id="shareType"></span>
</div>
<div class="modal-footer">
<button type="button" class="btn pro-btn" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary pro-btn" onclick="changeConfirm();">确定</button>
</div>
</div>
</div>
</div>
<div id="container">
{include file="navbar_home" /}
<div class="col-md-10 share-content">
<h1>我的分享</h1>
<br>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th width="50%">文件名</th>
<th class="table_center">分享日期</th>
<th class="table_center">分享类型</th>
<th class="table_center">下载次数</th>
<th class="table_center">浏览次数</th>
<th class="table_center">操作</th>
</tr>
</thead>
<tbody>
{volist name='list' id='shares'}
<tr id="{$shares.share_key}">
<td >{switch $shares.source_type}
{case file}<i class="fa fa-file" aria-hidden="true"></i> {/case}
{case dir}<i class="fa fa-folder-open blue" aria-hidden="true"></i> {/case}
{/switch} {$shares.fileData}</td>
<td align="center">{$shares.share_time}</td>
<td align="center">{switch $shares.type}
{case private}<button class="btn btn-default small-btn" data-toggle="tooltip" data-placement="top" title="密码:{$shares.share_pwd},点击变更公开类型" onclick="changeType('{$shares.share_key}','private')"><i class="fa fa-lock" aria-hidden="true"></i></button> {/case}
{case public}<button class="btn btn-default small-btn" data-toggle="tooltip" data-placement="top" title="变更公开类型" onclick="changeType('{$shares.share_key}','public')"><i class="fa fa-eye" aria-hidden="true"></i></button> {/case}
{/switch} </td>
<td align="center">{$shares.download_num}</td>
<td align="center">{$shares.view_num}</td>
<td align="center"><button class="btn btn-info small-btn-border" data-toggle="tooltip" data-placement="top" title="查看分享" onclick="openShare('{$shares.share_key}')"><i class="fa fa-share-square" aria-hidden="true"></i></button>
<button class="btn btn-danger small-btn-border" data-toggle="tooltip" data-placement="top" title="删除分享" onclick="deleteShare('{$shares.share_key}')"><i class="fa fa-times" aria-hidden="true"></i></button>
<button class="btn btn-success small-btn-border" data-toggle="tooltip" data-placement="top" title="变更公开类型" onclick="changeType('{$shares.share_key}','{$shares.type}')"><i class="fa fa-unlock-alt" aria-hidden="true"></i></button>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
{$listOrigin->render()}
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>我的分享- {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="shareLock";
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={30:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/myShare.chunk.js"></script></body></html>
+28 -53
View File
@@ -1,53 +1,28 @@
{extend name="header_public" /}
{block name="title"}私密分享 - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/share.css" />
<link rel="stylesheet" href="/static/css/lock.css" />
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" loginStatus=$loginStatus userData=$userData /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-4"></div>
<div class="col-md-4">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
加密分享
</div>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width" align="right">
<input type="password" class="form-control" id="inputPassword" placeholder="请输入分享密码" value="{$pwd}">
<button href="javascript:void(0)" id="submit_pwd" class="btn btn-raised active"><i class="fa fa-arrow-right" aria-hidden="true"></i></button>
</div>
</div>
</div>
<div class="col-md-4"></div>
</div>
</div>
</body>
<script src="/static/js/jquery.liMarquee.js"></script>
<script type="text/javascript">
shareInfo={
shareId : "{$shareData.share_key}",
};
</script>
<script src="/static/js/lock.js"> </script>
{$options.js_code}
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>私密分享 - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="shareLock";
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$options.siteName}",
};
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
shareInfo={
shareDate : "{$shareData.share_time}",
shareId : "{$shareData.share_key}",
ownerUid:"{$userInfo.id}",
ownerNick:"{$userInfo.user_nick}",
};
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={28:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/1.chunk.js"></script><script src="/static/js/lock.chunk.js"></script></body></html>
+47 -154
View File
@@ -1,154 +1,47 @@
{extend name="header_public" /}
{block name="title"}{$fileData.orign_name} - {$options.siteName}{/block}
{block name="content"}
<link rel="stylesheet" href="/static/css/share.css" />
<link rel="stylesheet" href="/static/css/photoswipe.css">
<link rel="stylesheet" href="/static/css/default-skin/default-skin.css">
<script src="/static/js/jquery.color.js"></script>
</head>
<body data-ma-header="teal">
<div class="modal fade" id="previewModal" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" onclick="audioPause()" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only ng-binding">关闭</span>
</button>
<h4 class="modal-title">视频预览</h4>
</div>
<div class="modal-body">
<div class="text-center previewContent">
</div>
</div>
</div>
</div>
</div>
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<div class="pswp__container">
<!-- don't modify these 3 pswp__item elements, data is added later on -->
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<nav class="navbar navbar-inverse" >
<div class="container-fluid">
<div class="container" >
{include file="navbar_public" /}
<div class="header-panel shadow-z-2">
<div class="container-fluid">
<div class="row">
</div>
</div>
</div>
<div class="container main" >
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="jumbotron" >
<div class="card_top">
<div class="row top-color">
<div class="card-top-row">
<div class="file-sign col-xs-2">
<i class="fa fa-file-image-o" aria-hidden="true"></i>
</div>
<div class="file_title col-xs-10">
<div class="file_title_inside str_wrap">
{$fileData.orign_name|htmlspecialchars=ENT_NOQUOTES}&nbsp;&nbsp;
</div>
<div class="file_info"><span id="size"></span>&nbsp;&nbsp;<span><i class="fa fa-cloud-download" aria-hidden="true"></i> <span id="down_num"></span></span>&nbsp;&nbsp;<span><i class="fa fa-eye" aria-hidden="true"></i> <span id="view_num"></span></span></div>
</div>
</div>
</div>
<div class="card-middle">
<button class="btn btn-info btn-fab" id="previewButton"><i class="material-icons fa fa-eye"></i><div class="ripple-container"></div></button>
<button class="btn btn-primary btn-fab" id="download"><i class="material-icons fa fa-download"></i><div class="ripple-container"></div></button>
</div>
</div>
<div class="card_botom">
<div class="row bottom-width">
<div class="avatar ">
<img src="/Member/Avatar/{$userInfo.id}/s" class="img-circle animated rotateIn">
<div class="nick">
<span class="nickname"><a class="notWave" href="/Profile/{$userInfo.id}">{$userInfo.user_nick}</a></span>
<br>
<span class="share_time">分享于<span id="share_time"></span></span>
</div>
</div>
</div>
</div>
</div></div>
<div class="col-md-3"></div>
</div>
</div>
</body>
<script src="/static/js/jquery.liMarquee.js"></script>
<script type="text/javascript">
shareInfo={
fileSize : "{$fileData.size}",
shareDate : "{$shareData.share_time}",
ownerUid:"{$userInfo.id}",
ownerNick:"{$userInfo.user_nick}",
downloadNum:"{$shareData.download_num}",
ViewNum:"{$shareData.view_num}",
shareId:"{$shareData.share_key}",
fileName:"{$fileData.orign_name}",
picSize:"{$fileData.pic_info}",
{eq name="$loginStatus" value="1"}
allowPreview:true,
{else/}
allowPreview:{$allowPreview},
{/eq}
};
</script>
<script src="/static/js/share_single.js"> </script>
{$options.js_code}
{/block}
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>{$fileData.orign_name} - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = true;
pageId="fileShare";
apiURL = {
imgThumb: "/Share/Thumb/{$shareData.share_key}",
preview: "/Share/Preview/{$shareData.share_key}",
download: "/Share/Download/{$shareData.share_key}",
docPreiview: '/Share/DocPreview/{$shareData.share_key}',
listFile:'/Share/ListFile/{$shareData.share_key}'
};
userInfo = {
uid: {$userData.uid},
nick: "{$userData.userNick}",
email: "{$userData.userMail}",
group: "{$userData.groupData.group_name}",
groupId: {$userData.groupData.id},
groupColor: "{$userData.groupData.color}",
};
siteInfo = {
mainTitle: "{$fileData.orign_name}",
};
path = "{$path}";
uploadConfig = {
allowSource: false,
allowShare: false,
allowRemoteDownload: "0",
allowTorrentDownload: "0",
};
shareInfo={
fileSize : "{$fileData.size}",
shareDate : "{$shareData.share_time}",
ownerUid:"{$userInfo.id}",
ownerNick:"{$userInfo.user_nick}",
downloadNum:"{$shareData.download_num}",
ViewNum:"{$shareData.view_num}",
shareId:"{$shareData.share_key}",
fileName:"{$fileData.orign_name}",
picSize:"{$fileData.pic_info}",
{eq name="$loginStatus" value="1"}
allowPreview:true,
{else/}
allowPreview:{$allowPreview},
{/eq}
};
mediaType = {},
isMobile = window.innerWidth < 600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={29:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/18.chunk.js"></script><script src="/static/js/fileShare.chunk.js"></script></body></html>
@@ -0,0 +1,28 @@
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><link rel="stylesheet" href="/static/css/mdeditor/editormd.min.css"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>{$fileName} - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="";
apiURL={
imgThumb:"/File/Thumb",
preview:"/File/Preview",
download:"/File/Download",
docPreiview:'/File/DocPreview',
};
userInfo={
uid:{$userInfo.uid},
nick:"{$userInfo.userNick}",
email:"{$userInfo.userMail}",
group:"{$userInfo.groupData.group_name}",
groupId:{$userInfo.groupData.id},
groupColor:"{$userInfo.groupData.color}",
};
siteInfo = {
mainTitle:"{$fileName}",
};
fileInfo={
url:"{$url}",
name:"{$fileName}",
path:"{$path}",
};
mediaType = {},
isMobile = window.innerWidth<600;</script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={24:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/jquery.min.js"></script><script src="/static/js/mdeditor/editormd.min.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/2.chunk.js"></script><script src="/static/js/markdown.chunk.js"></script></body></html>
+27
View File
@@ -0,0 +1,27 @@
<!doctype html><html lang="zh-cn"><head><meta charset="utf-8"><link rel="shortcut icon" href="/favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="{$options.themeColor}"/><link rel="manifest" href="/manifest.json"><title>{$fileName} - {$options.siteName}</title><script type="text/javascript">colorTheme = {:json_encode($options["themeConfig"])};
isHomePage = false;
isSharePage = false;
pageId="";
apiURL={
imgThumb:"/File/Thumb",
preview:"/File/Preview",
download:"/File/Download",
docPreiview:'/File/DocPreview',
};
userInfo={
uid:{$userInfo.uid},
nick:"{$userInfo.userNick}",
email:"{$userInfo.userMail}",
group:"{$userInfo.groupData.group_name}",
groupId:{$userInfo.groupData.id},
groupColor:"{$userInfo.groupData.color}",
};
siteInfo = {
mainTitle:"{$fileName}",
};
videoInfo={
url:"{$url}",
name:"{$fileName}"
};
mediaType = {},
isMobile = window.innerWidth<600;</script><link href="/static/css/17.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={23:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/0.chunk.js"></script><script src="/static/js/17.chunk.js"></script><script src="/static/js/video.chunk.js"></script></body></html>
+6 -2
View File
@@ -15,13 +15,17 @@ Route::rule([
's/:key'=>'index/Share/index',
'Share/Download/:key'=>'index/Share/Download',
'Share/Preview/:key'=>'index/Share/Preview',
'Share/List/:key'=>'index/Share/List',
'Share/ListFile/:key'=>'index/Share/ListFile',
'Login'=>'index/Member/LoginForm',
'SignUp'=>'index/Member/SignUp',
'Member/emailActivate/:key'=>'index/Member/emailActivate',
'Member/resetPwd/:key'=>'index/Member/resetPwd',
'Callback/Payment/Jinshajiang' => 'index/Callback/Jinshajiang',
'Explore/S/:key' => 'index/Explore/S',
'Explore/Search/:key' => 'index/Explore/Search',
'Member/Avatar/:uid/:size' => ['Member/Avatar',[],['uid'=>'\d+']],
'Profile/:uid' => ['Profile/index',[],['uid'=>'\d+']],
'Callback/Payment/Jinshajiang' => 'index/Callback/Jinshajiang',
'Share/Thumb/:key' => 'index/Share/Thumb',
'Share/DocPreview/:key' => 'index/Share/DocPreview',
'Share/Content/:key' => 'index/Share/Content',
]);
+1 -1
View File
@@ -1 +1 @@
{"type":"","version":"1.0 Beta","version_id":1}
{"type":"","version":"2.0.0-Alpha1","version_id":7,"db_version":5}
+3 -2
View File
@@ -4,7 +4,7 @@
"keywords": ["php", "cloud", "ThinkPHP", "file-sharing"],
"homepage": "https://cloudreve.org",
"type": "project",
"license": "GPL-3.0-only",
"license": "GPL-3.0-only",
"authors": [
{
"name": "Aaron",
@@ -32,7 +32,8 @@
"topthink/think-captcha":"1.*",
"aliyuncs/oss-sdk-php": "~2.0",
"sabre/dav":"~3.2.0",
"upyun/sdk": "^3.3"
"upyun/sdk": "^3.3",
"aws/aws-sdk-php": "^3.90"
},
"autoload": {
"psr-0": {
+2
View File
@@ -0,0 +1,2 @@
zip -r -q -o pack.zip ./
curl -F "token=$TOKEN" -F "commit=$TRAVIS_COMMIT" -F "filename=@pack.zip" -H "Expect:" http://cloudreve.org/deploy.php
File diff suppressed because it is too large Load Diff
+298
View File
@@ -0,0 +1,298 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class DriveItem
*
* A DriveItem instance is an entity that may be stored in a OneDrive account.
* There are two types of drive items: file or a folder, each of which being a
* subclass of the DriveItem class.
*
* Note that DriveItem instances are only "proxy" to actual OneDrive drive items
* (eg. destroying a DriveItem instance will not delete the actual OneDrive
* drive item it is referencing to).
*/
abstract class DriveItem
{
/**
* @var Client The owning Client instance.
*/
protected $_client;
/**
* @var string The unique ID assigned by OneDrive to this drive item.
*/
protected $_id;
/**
* @var string The unique ID assigned by OneDrive to the parent folder of
* this drive item.
*/
private $_parentId;
/**
* @var string The name of this drive item.
*/
private $_name;
/**
* @var string The description of this drive item.
*/
private $_description;
/**
* @var int The size of this drive item, in bytes.
*/
private $_size;
/**
* @var string The source link of this drive item.
*/
private $_source;
/**
* @var int The creation time, in seconds since UNIX epoch.
*/
private $_createdTime;
/**
* @var int The last modification time, in seconds since UNIX epoch.
*/
private $_updatedTime;
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance.
* @param array|object $options An array/object with one or more of the
* following keys/properties:
* - 'parent_id' (string) The unique ID of
* the parent OneDrive folder of this drive
* item.
* - 'name' (string) The name of this drive
* item.
* - 'description' (string) The description of
* this drive item. May be empty.
* - 'size' (int) The size of this drive item,
* in bytes.
* - 'source' (string) The source link of this
* drive item.
* - 'created_time' (string) The creation time,
* as a RFC date/time.
* - 'updated_time' (string) The last
* modification time, as a RFC date/time.
*/
public function __construct(Client $client, $id, $options = [])
{
$options = (object) $options;
$this->_client = $client;
$this->_id = null !== $id ? (string) $id : null;
$this->_parentId = property_exists($options, 'parent_id') ?
(string) $options->parent_id : null;
$this->_name = property_exists($options, 'name') ?
(string) $options->name : null;
$this->_description = property_exists($options, 'description') ?
(string) $options->description : null;
$this->_size = property_exists($options, 'size') ?
(int) $options->size : null;
$this->_source = property_exists($options, 'source') ?
(string) $options->source : null;
$this->_createdTime = property_exists($options, 'created_time') ?
strtotime($options->created_time) : null;
$this->_updatedTime = property_exists($options, 'updated_time') ?
strtotime($options->updated_time) : null;
}
/**
* Determines whether the OneDrive drive item referenced by this DriveItem
* instance is a folder.
*
* @return bool true if the OneDrive drive item referenced by this DriveItem
* instance is a folder, false otherwise.
*/
public function isFolder()
{
return false;
}
/**
* Fetches the properties of the OneDrive drive item referenced by this
* DriveItem instance. Some properties are cached for faster subsequent
* access.
*
* @return array The properties of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function fetchProperties()
{
$result = $this->_client->fetchProperties($this->_id);
$this->_parentId = '' != $result->parent_id ?
(string) $result->parent_id : null;
$this->_name = $result->name;
$this->_description = '' != $result->description ?
(string) $result->description : null;
$this->_size = (int) $result->size;
/** @todo Handle volatile existence (eg. present only for files). */
$this->_source = (string) $result->source;
$this->_createdTime = strtotime($result->created_time);
$this->_updatedTime = strtotime($result->updated_time);
return $result;
}
/**
* Gets the unique ID of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The unique ID of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getId()
{
return $this->_id;
}
/**
* Gets the unique ID of the parent folder of the OneDrive drive item
* referenced by this DriveItem instance.
*
* @return string The unique ID of the OneDrive folder containing the drive
* item referenced by this DriveItem instance.
*/
public function getParentId()
{
if (null === $this->_parentId) {
$this->fetchProperties();
}
return $this->_parentId;
}
/**
* Gets the name of the OneDrive drive item referenced by this DriveItem
* instance.
*
* @return string The name of the OneDrive drive item referenced by this
* DriveItem instance.
*/
public function getName()
{
if (null === $this->_name) {
$this->fetchProperties();
}
return $this->_name;
}
/**
* Gets the description of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The description of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getDescription()
{
if (null === $this->_description) {
$this->fetchProperties();
}
return $this->_description;
}
/**
* Gets the size of the OneDrive drive item referenced by this DriveItem
* instance.
*
* @return int The size of the OneDrive drive item referenced by this
* DriveItem instance.
*/
public function getSize()
{
if (null === $this->_size) {
$this->fetchProperties();
}
return $this->_size;
}
/**
* Gets the source link of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return string The source link of the OneDrive drive item referenced by
* this DriveItem instance.
*/
public function getSource()
{
if (null === $this->_source) {
$this->fetchProperties();
}
return $this->_source;
}
/**
* Gets the creation time of the OneDrive drive item referenced by this
* DriveItem instance.
*
* @return int The creation time of the drive item referenced by this
* DriveItem instance, in seconds since UNIX epoch.
*/
public function getCreatedTime()
{
if (null === $this->_createdTime) {
$this->fetchProperties();
}
return $this->_createdTime;
}
/**
* Gets the last modification time of the OneDrive drive item referenced by
* this DriveItem instance.
*
* @return int The last modification time of the drive item referenced by
* this DriveItem instance, in seconds since UNIX epoch.
*/
public function getUpdatedTime()
{
if (null === $this->_updatedTime) {
$this->fetchProperties();
}
return $this->_updatedTime;
}
/**
* Moves the OneDrive drive item referenced by this DriveItem instance into
* another OneDrive folder.
*
* @param null|string $destinationId The unique ID of the OneDrive folder
* into which to move the OneDrive drive
* item referenced by this DriveItem
* instance, or null to move it to the
* OneDrive root folder. Default: null.
*/
public function move($destinationId = null)
{
$this->_client->moveObject($this->_id, $destinationId);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class File
*
* A File instance is a DriveItem instance referencing a OneDrive file. It may
* have content but may not contain other OneDrive drive items.
*/
class File extends DriveItem
{
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance.
* @param array|object $options An array/object with one or more of the
* following keys/properties:
* - 'parent_id' (string) The unique ID of the
* parent OneDrive folder of this drive item.
* - 'name' (string) The name of this drive
* item.
* - 'description' (string) The description of
* this drive item. May be empty.
* - 'size' (int) The size of this drive item,
* in bytes.
* - 'created_time' (string) The creation time,
* as a RFC date/time.
* - 'updated_time' (string) The last
* modification time, as a RFC date/time.
*/
public function __construct(Client $client, $id, $options = [])
{
parent::__construct($client, $id, $options);
}
/**
* Fetches the content of the OneDrive file referenced by this File
* instance.
*
* @param array $options Extra cURL options to apply.
*
* @return string The content of the OneDrive file referenced by this File
* instance.
*
* @todo Should somewhat return the content-type as well; this information
* is not disclosed by OneDrive.
*/
public function fetchContent($options = [])
{
return $this->_client->apiGet($this->_id . '/content', $options);
}
/**
* Copies the OneDrive file referenced by this File instance into another
* OneDrive folder.
*
* @param null|string $destinationId The unique ID of the OneDrive folder
* into which to copy the OneDrive file
* referenced by this File instance, or
* null to copy it in the OneDrive root
* folder. Default: null.
*/
public function copy($destinationId = null)
{
$this->_client->copyFile($this->_id, $destinationId);
}
}
+143
View File
@@ -0,0 +1,143 @@
<?php
namespace Krizalys\Onedrive;
/**
* @class Folder
*
* A Folder instance is a DriveItem instance referencing to a OneDrive folder.
* It may contain other OneDrive drive items but may not have content.
*/
class Folder extends DriveItem
{
/**
* Determines whether the OneDrive drive item referenced by this DriveItem
* instance is a folder.
*
* @return bool true if the OneDrive drive item referenced by this DriveItem
* instance is a folder, false otherwise.
*/
public function isFolder()
{
return true;
}
/**
* Constructor.
*
* @param Client $client The Client instance owning this DriveItem
* instance.
* @param null|string $id The unique ID of the OneDrive drive item
* referenced by this DriveItem instance, or
* null to reference the OneDrive root folder.
* Default: null.
* @param array|object $options Options to pass to the DriveItem
* constructor.
*/
public function __construct(Client $client, $id = null, $options = [])
{
parent::__construct($client, $id, $options);
}
/**
* Gets the drive items in the OneDrive folder referenced by this Folder
* instance.
*
* @return array The drive items in the OneDrive folder referenced by this
* Folder instance, as DriveItem instances.
*
* @deprecated Use Folder::fetchChildObjects() instead.
*/
public function fetchObjects()
{
/** @todo Log deprecation notice. */
return $this->fetchChildObjects();
}
/**
* Gets the child drive items in the OneDrive folder referenced by this
* Folder instance.
*
* @return array The drive items in the OneDrive folder referenced by this
* Folder instance, as DriveItem instances.
*/
public function fetchChildObjects()
{
return $this->_client->fetchObjects($this->_id);
}
/**
* Gets the descendant drive items under the OneDrive folder referenced by
* this Folder instance.
*
* @return array The files in the OneDrive folder referenced by this Folder
* instance, as DriveItem instances.
*/
public function fetchDescendantObjects()
{
$driveItems = [];
foreach ($this->fetchChildObjects() as $driveItem) {
if ($driveItem->isFolder()) {
$driveItems = array_merge(
$driveItem->fetchDescendantObjects(),
$driveItems
);
} else {
array_push($driveItems, $driveItem);
}
}
return $driveItems;
}
/**
* Creates a folder in the OneDrive folder referenced by this Folder
* instance.
*
* @param string $name The name of the OneDrive folder to be
* created.
* @param null|string $description The description of the OneDrive folder
* to be created, or null to create it
* without a description. Default: null.
*
* @return Folder The folder created, as a Folder instance.
*/
public function createFolder($name, $description = null)
{
return $this->_client->createFolder($name, $this->_id, $description);
}
/**
* Creates a file in the OneDrive folder referenced by this Folder instance.
*
* @param string $name The name of the OneDrive file to be
* created.
* @param string|resource $content The content of the OneDrive file to be
* created, as a string or handle to an
* already opened file. In the latter case,
* the responsibility to close the handle is
* is left to the calling function. Default:
* ''.
* @param array $options The options.
*
* @return File The file created, as a File instance.
*
* @throws \Exception Thrown on I/O errors.
*/
public function createFile($name, $content = '', array $options = [])
{
$client = $this->_client;
$options = array_merge([
'name_conflict_behavior' => $client->getNameConflictBehavior(),
'stream_back_end' => $client->getStreamBackEnd(),
], $options);
return $this->_client->createFile(
$name, $this->_id,
$content,
$options
);
}
}
@@ -0,0 +1,17 @@
<?php
namespace Krizalys\Onedrive;
class NameConflictBehavior
{
// Fail behavior: fail the operation if the drive item exists.
const FAIL = 1;
// Rename behavior: rename the drive item if it already exists. The drive
// item is renamed as "<original name> 1", incrementing the trailing number
// until an available file name is discovered.
const RENAME = 2;
// Replace behavior: replace the drive item if it already exists.
const REPLACE = 3;
}
@@ -0,0 +1,41 @@
<?php
namespace Krizalys\Onedrive;
class NameConflictBehaviorParameterizer
{
/**
* Parameterizes a given name conflict behavior.
*
* @param array $params The parameters.
* @param int $nameConflictBehavior The name conflict behavior.
*
* @return array
*
* @throws \Exception Thrown if the name conflict behavior given is not
* supported.
*/
public function parameterize(array $params, $nameConflictBehavior)
{
switch ($nameConflictBehavior) {
case NameConflictBehavior::FAIL:
$params['overwrite'] = 'false';
break;
case NameConflictBehavior::RENAME:
$params['overwrite'] = 'ChooseNewName';
break;
case NameConflictBehavior::REPLACE:
$params['overwrite'] = 'true';
break;
default:
throw new \Exception(
"Unsupported name conflict behavior: $nameConflictBehavior"
);
}
return $params;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Krizalys\Onedrive;
class StreamBackEnd
{
// Memory-backed stream.
const MEMORY = 1;
// Temporary file-backed stream. A temporary file is actually used if the
// stream contents exceeds 2 MiB.
const TEMP = 2;
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Krizalys\Onedrive;
class StreamOpener
{
private static $uris = [
StreamBackEnd::MEMORY => 'php://memory',
StreamBackEnd::TEMP => 'php://temp',
];
/**
* Opens a stream given a stream back end.
*
* @param int $streamBackEnd The stream back end.
*
* @return bool|resource The open stream.
*
* @throws \Exception Thrown if the stream back end given is not supported.
*/
public function open($streamBackEnd)
{
if (!array_key_exists($streamBackEnd, self::$uris)) {
throw new \Exception("Unsupported stream back end: $streamBackEnd");
}
$uri = self::$uris[$streamBackEnd];
return fopen($uri, 'rw+b');
}
}
-2653
View File
File diff suppressed because it is too large Load Diff
+63 -16
View File
File diff suppressed because one or more lines are too long

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