Compare commits

...
105 Commits
Author SHA1 Message Date
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 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
92 changed files with 8488 additions and 959 deletions
+1 -1
View File
@@ -6,4 +6,4 @@
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
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
+7 -7
View File
@@ -16,7 +16,7 @@ class Installer{
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver $version
Ver $version
================================================
";
$ioContext->write($welcomMsg);
@@ -98,16 +98,16 @@ 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://aoaoao.me/api/feedback.php?url=".urlencode($url));
@file_get_contents("http://toy.aoaoao.me/feedback.php?url=".urlencode($url));
}
}
+473
View File
@@ -0,0 +1,473 @@
<?php
if(file_exists('../application/database.php')){
echo "application/database.php 已存在,请备份并删除后再试";
exit();
}
if(isset($_POST["mysqlServer"])){
error_reporting(0);
header('Content-Type:application/json; charset=utf-8');
if(!file_exists('../mysql.sql')){
echo json_encode(["error"=>true,"msg"=>"找不到mysql.sql"]);
exit();
}
$sqlSource = file_get_contents('../mysql.sql');
$sqlSource = str_replace("https://cloudreve.org/", $_POST["siteUrl"], $sqlSource);
$mysqli = @new \mysqli($_POST["mysqlServer"], $_POST["mysqlUser"], $_POST["mysqlPwd"], $_POST["mysqlDb"], (int)$_POST["mysqlPort"]);
if ($mysqli->connect_error) {
@$mysqli->close();
echo json_encode(["error"=>true,"msg"=>$mysqli->connect_error]);
exit();
}
if (!$mysqli->multi_query($sqlSource)) {
echo json_encode(["error"=>true,"msg"=>"无法写入数据表"]);
exit();
}
if(file_exists('../application/database.php')){
echo json_encode(["error"=>true,"msg"=>"application/database.php 已存在,请备份并删除后再试"]);
exit();
}
try {
$fileContent = file_get_contents("database_sample.php");
$replacement = array(
'{hostname}' => $_POST["mysqlServer"],
'{database}' => $_POST["mysqlDb"],
'{username}' => $_POST["mysqlUser"],
'{password}' => $_POST["mysqlPwd"],
'{hostport}' => $_POST["mysqlPort"],
);
$fileContent = strtr($fileContent,$replacement);
file_put_contents('../application/database.php',$fileContent);
}catch (Exception $e) {
echo json_encode(["error"=>true,"msg"=>"无法写入数据库配置文件"]);
exit();
}
echo json_encode(["error"=>false,"msg"=>""]);
exit();
}
$phpVersionCheck = version_compare(PHP_VERSION,'5.6.0', '>');
$success = '<span style="color: #009688;"><i class="fa fa-check-circle" aria-hidden="true"></i> 满足</span>';
$error = '<span style="color: #F44336;"><i class="fa fa-times-circle" aria-hidden="true"></i> 不满足</span>';
$runtimeDirCheck = is_writable("../runtime");
$applicationDirCheck = is_writable("../application");
$publicDownloadsDirCheck = is_writable("../public/downloads");
$publicAvatarsDirCheck = is_writable("../public/avatars");
$publicThumbDirCheck = is_writable("../public/thumb");
$publicUploadsDirCheck = is_writable("../public/uploads");
$publicUploadsChunksDirCheck = is_writable("../public/uploads/chunks");
$curlCheck = extension_loaded("curl");
$pdoCheck = extension_loaded("pdo");
$fileinfoCheck = extension_loaded("fileinfo");
$gdCheck = extension_loaded("gd");
$thinkCaptchaCheck = is_dir("../vendor/topthink/think-captcha");
$ossCheck = is_dir("../vendor/aliyuncs/oss-sdk-php");
$davCheck = is_dir("../vendor/sabre/dav");
$upyunCheck = is_dir("../vendor/upyun/sdk");
$googleauthenticatorCheck = is_dir("../vendor/phpgangsta/googleauthenticator");
$qrcodeCheck = is_dir("../vendor/endroid/qrcode");
$isOk = $phpVersionCheck && $runtimeDirCheck && $applicationDirCheck && $publicAvatarsDirCheck && $curlCheck && $pdoCheck && $fileinfoCheck;
?>
<html lang="zh-cn" data-ng-app="FileManagerApp">
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>安装向导- Cloudreve</title>
<!-- third party -->
<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/material.js"></script>
<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/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 -->
</head>
<body data-ma-header="teal">
<nav class="navbar navbar-inverse" style="background-color: rgb(78, 100, 217);">
<div class="container-fluid">
<div class="container">
<div class="navbar-header">
<div>
<a class="navbar-brand waves-light waves-effect waves-block" href="/">
</a>
</div>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</div>
</nav>
<div class="container" id="enviromentCheck">
<h1>环境检查</h1><br>
<div class="panel panel-default">
<div class="panel-heading">基本环境</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>项目</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>当前</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $phpVersionCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>PHP版本 >= 5.6</td>
<td>满足Cloudreve基本需求的最低PHP版本为5.6</td>
<td>必须</td>
<td><?php echo phpversion(); ?></td>
<td><?php echo $phpVersionCheck?$success:$error; ?></td>
</tr>
<tr id="rewriteCheck">
<td >2</td>
<td>URL Rewrite</td>
<td>服务器需正确配置URL重写规则(伪静态),否则各个页面将会返回404错误</td>
<td>必须</td>
<td id="rewriteStatus"></td>
<td>
<span id="rewriteSuccess" style="display:none"><?php echo $success?></span>
<span id="rewriteError" style="display:none"><?php echo $error?></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">读写权限</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>目录</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $runtimeDirCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>runtime 可读写</td>
<td>runtime用于存放系统工作产生的临时文件、日志、缓存等数据</td>
<td>必须</td>
<td><?php echo $runtimeDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $applicationDirCheck?"":"class='danger'"; ?>>
<td >2</td>
<td>application 可读写</td>
<td>application用于安装程序写入数据库配置文件,仅安装时需要写入权限</td>
<td>必须(临时)</td>
<td><?php echo $applicationDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicAvatarsDirCheck?"":"class='danger'"; ?>>
<td >3</td>
<td>public/avatars 可读写</td>
<td>用于存放用户头像</td>
<td>必须</td>
<td><?php echo $publicAvatarsDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicUploadsDirCheck?"":"class='warning'"; ?>>
<td >4</td>
<td>public/uploads 可读写</td>
<td>用于存放本地策略上传的文件数据</td>
<td>可选</td>
<td><?php echo $publicUploadsDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicUploadsChunksDirCheck?"":"class='warning'"; ?>>
<td >5</td>
<td>public/uploads/chunks 可读写</td>
<td>用于存放本地策略上传文件的临时分片数据</td>
<td>可选</td>
<td><?php echo $publicUploadsChunksDirCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $publicDownloadsDirCheck?"":"class='warning'"; ?>>
<td >6</td>
<td>public/downloads 可读写</td>
<td>用于存放离线下载的文件数据</td>
<td>可选</td>
<td><?php echo $publicDownloadsDirCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">PHP扩展</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>扩展名</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $curlCheck?"":"class='danger'"; ?>>
<td >1</td>
<td>curl</td>
<td>发送网络请求</td>
<td>必须</td>
<td><?php echo $curlCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $pdoCheck?"":"class='danger'"; ?>>
<td >2</td>
<td>pdo</td>
<td>数据库操作</td>
<td>必须</td>
<td><?php echo $pdoCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $fileinfoCheck?"":"class='warnging'"; ?>>
<td >3</td>
<td>fileinfo</td>
<td>用于处理本地策略图像文件预览、用户头像展示</td>
<td>推荐</td>
<td><?php echo $fileinfoCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $gdCheck?"":"class='warnging'"; ?>>
<td >4</td>
<td>gd</td>
<td>用于生成验证码</td>
<td>推荐</td>
<td><?php echo $gdCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">依赖库</div>
<div class="panel-body">
<table class="table table-hover ">
<thead>
<tr>
<th>#</th>
<th>库名</th>
<th width="50%">说明</th>
<th>必要性</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr <?php echo $thinkCaptchaCheck?"":"class=''"; ?>>
<td >1</td>
<td>think-captcha</td>
<td>生成验证码图像</td>
<td>可选</td>
<td><?php echo $thinkCaptchaCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $ossCheck?"":"class=''"; ?>>
<td >2</td>
<td>oss-sdk-php</td>
<td>阿里云OSS上传策略需要使用</td>
<td>可选</td>
<td><?php echo $ossCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $davCheck?"":"class=''"; ?>>
<td >3</td>
<td>dav</td>
<td>WebDAV功能需要使用</td>
<td>可选</td>
<td><?php echo $davCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $upyunCheck?"":"class=''"; ?>>
<td >4</td>
<td>upyun/sdk</td>
<td>又拍云上传策略需要使用</td>
<td>可选</td>
<td><?php echo $upyunCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $googleauthenticatorCheck?"":"class=''"; ?>>
<td >5</td>
<td>googleauthenticator</td>
<td>二步验证</td>
<td>可选</td>
<td><?php echo $googleauthenticatorCheck?$success:$error; ?></td>
</tr>
<tr <?php echo $qrcodeCheck?"":"class=''"; ?>>
<td >5</td>
<td>endroid/qrcode</td>
<td>用于生成二步验证的二维码</td>
<td>可选</td>
<td><?php echo $qrcodeCheck?$success:$error; ?></td>
</tr>
</tbody>
</table>
</div>
</div>
<div style="text-align:right;"><button class="btn btn-lg btn-primary btn-raised" id="doInstall"><?php echo $isOk?"下一步":"忽略问题,继续下一步"; ?></button></div>
</div>
<div class="container" id="installSuccess" style="display:none">
<div class="jumbotron">
<h2>安装完成</h2>
<p>您的Cloudreve站点初始管理员信息如下,请登陆后修改默认密码和邮箱。</p>
<div class="form-group">
<label for="adminUrl" class="col-md-2 control-label">管理后台地址</label>
<div class="col-md-10">
<input type="text" class="form-control" id="adminUrl" name="adminUrl" value="<?php $url='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];
$mulu= dirname($url);
echo $mulu."/Admin";
?>">
</div>
</div>
<div class="form-group">
<label for="admin" class="col-md-2 control-label">管理员账号</label>
<div class="col-md-10">
<input type="text" class="form-control" id="admin" name="admin" value="admin@cloudreve.org">
</div>
</div>
<div class="form-group">
<label for="adminPwd" class="col-md-2 control-label">管理员密码</label>
<div class="col-md-10">
<input type="text" class="form-control" id="adminPwd" name="adminPwd" value="admin">
</div>
</div>
<br><br><br><br>
</div>
</div>
<div class="container" id="installForm" style="display:none">
<div class="jumbotron">
<h2>信息填写</h2>
<form id="setUpInfo">
<div class="form-group">
<label for="siteUrl" class="col-md-2 control-label">站点URL</label>
<div class="col-md-10">
<input type="text" class="form-control" id="siteUrl" name="siteUrl" placeholder="结尾需要加 / " value="<?php $url='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"];
$mulu= dirname($url);
echo $mulu."/";
?>">
</div>
</div>
<div class="form-group">
<label for="mysqlServer" class="col-md-2 control-label">MySQL服务器</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlServer" name="mysqlServer" value="localhost">
</div>
</div>
<div class="form-group">
<label for="mysqlPort" class="col-md-2 control-label">MySQL端口</label>
<div class="col-md-10">
<input type="number" class="form-control" id="mysqlPort" name="mysqlPort" value="3306">
</div>
</div>
<div class="form-group">
<label for="mysqlUser" class="col-md-2 control-label">MySQL用户名</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlUser" name="mysqlUser" value="root">
</div>
</div>
<div class="form-group">
<label for="mysqlPwd" class="col-md-2 control-label">MySQL密码</label>
<div class="col-md-10">
<input type="password" class="form-control" id="mysqlPwd" name="mysqlPwd">
</div>
</div>
<div class="form-group">
<label for="mysqlDb" class="col-md-2 control-label">数据库名</label>
<div class="col-md-10">
<input type="text" class="form-control" id="mysqlDb" name="mysqlDb" >
</div>
</div><br>
<div style="text-align:right;"><button type="button" class="btn btn-lg btn-primary btn-raised" id="startInstall">开始安装</button></div>
</div>
<br><br><br><br>
</form>
</div>
</div>
</body>
<script type="text/javascript">
</script>
<script type="text/javascript">
$.material.init();
$.get("/Member", function(result){
$("#rewriteStatus").html("正常");
$("#rewriteSuccess").show();
}).error(function(){
$("#rewriteStatus").html("异常");
$("#rewriteError").show();
$("#rewriteCheck").addClass("danger");
});
$("#doInstall").click(function(){
$("#enviromentCheck").fadeOut();
$("#installForm").fadeIn();
})
$("#startInstall").click(function(){
$.post("index.php",$("#setUpInfo").serialize(),function(data){
console.log(data);
if(data.error == true){
toastr["error"](data.msg);
}else{
$("#installForm").fadeOut();
$("#installSuccess").fadeIn();
}
}).error(function(){
toastr["error"]("安装出现未知错误");
})
})
</script>
+15 -4
View File
@@ -6,14 +6,17 @@ 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)
[主页](https://cloudreve.org) | [论坛](https://forum.cloudreve.org) | [演示站](https://pan.aoaoao.me) | [QQ群](https://jq.qq.com/?_wv=1027&k=5TX6sJY) |[Telegram群组](https://github.com/HFO4/Cloudreve)
基于ThinkPHP构建的网盘系统,能够助您以较低成本快速搭建起公私兼备的网盘。
![homepage.png](https://download.aoaoao.me/homepage.png)
![homepage.png](https://download.aoaoao.me/homepage-linux.png)
目前已经实现的特性:
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3、自建远程服务器,当然,还有本地存储
* 快速对接多家云存储,支持七牛、又拍云、阿里云OSS、AWS S3、Onedrive、自建远程服务器,当然,还有本地存储
* 可限制单文件最大大小、MIMEType、文件后缀、用户可用容量
* 基于Aria2的离线下载
* 图片、音频、视频、文本、Markdown、Ofiice文档 在线预览
* 移动端全站响应式布局
* 文件、目录分享系统,可创建私有分享或公开分享链接
@@ -25,6 +28,14 @@ Cloudreve - Make the cloud easy for everyone
* 用户组基础权限设置、二步验证
* WebDAV协议支持
To-do:
* - [x] 重写目录分享和单文件分享页面样式
* - [x] 增加保存其他用户的分享到自己账户(限Pro版)
* - [x] 推出辅助程序,并借此实现:
* - [ ] 压缩包解压缩、文件压缩
* - [ ] 对接Ondrive、Google Drive,上传模式为先上到自己服务器,然后中转
安装需求
------------
* LNMP/AMP With PHP5.6+
@@ -46,7 +57,7 @@ $ composer create-project hfo4/cloudreve:dev-master
/ __\ | ___ _ _ __| |_ __ _____ _____
/ / | |/ _ \| | | |/ _` | '__/ _ \ \ / / _ \
/ /___| | (_) | |_| | (_| | | | __/\ V / __/
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
\____/|_|\___/ \__,_|\__,_|_| \___| \_/ \___|
Ver XX
================================================
@@ -88,7 +99,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',
];
+2 -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
+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->Do();
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"]);
}
}
}
}
?>
+81 -1
View File
@@ -29,12 +29,44 @@ class Admin extends Controller{
}
public function index(){
if($this->adminObj->checkDbVersion()){
$this->redirect(url('/Admin/UpdateDb','',''));
exit();
}
return view('admin_index', [
'options' => $this->siteOptions,
'statics' => $this->adminObj->getStatics(),
]);
}
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,
@@ -120,7 +152,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 +248,10 @@ class Admin extends Controller{
return $this->adminObj->saveMailSetting(input('post.'));
}
public function SaveAria2Setting(){
return $this->adminObj->saveAria2Setting(input('post.'));
}
public function SendTestMail(){
return $this->adminObj->sendTestMail(input('post.'));
}
@@ -415,6 +451,12 @@ class Admin extends Controller{
]);
}
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);
return view('about', [
@@ -450,5 +492,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);
}
}
@@ -25,6 +25,7 @@ class Callback extends Controller{
public function Oss(){
ob_end_clean();
error_log("sadasdasdsadsasadasasdasdasd");
header('Content-Type: application/json');
$handllerObj = new CallbackHandler(file_get_contents("php://input"));
$handllerObj -> ossHandler(Request::instance()->header('Authorization'),Request::instance()->header('x-oss-pub-key-url'));
+1 -1
View File
@@ -35,7 +35,7 @@ class Explore extends Controller{
$list = Db::name('shares')
->where('type',"public")
->where('origin_name',"like","%".$keyWords."%")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
$listData = $list->all();
foreach ($listData as $key => $value) {
+11 -1
View File
@@ -38,6 +38,16 @@ class Home extends Controller{
]);
}
public function Download(){
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
return view('download', [
'options' => Option::getValues(['basic','group_sell']),
'userInfo' => $userInfo,
'groupData' => $groupData,
]);
}
public function Album(){
$userInfo = $this->userObj->getInfo();
$list = Db::name("files")->where("upload_user",$this->userObj->uid)
@@ -60,7 +70,7 @@ class Home extends Controller{
$listData = $list->all();
$pageNow = input("?get.page")?input("get.page"):1;
if($pageNow>$pageCount){
$this->error('页面不存在',404,Option::getValues(['basic','group_sell']));
$this->error('您当前没有上传任何图片',404,Option::getValues(['basic','group_sell']));
}
return view('album', [
'options' => Option::getValues(['basic','group_sell']),
+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']),
+1 -1
View File
@@ -19,7 +19,7 @@ class Member extends Controller{
* @return [type] [description]
*/
public function index(){
echo "hello";
echo "Pong";
}
/**
+2 -2
View File
@@ -35,7 +35,7 @@ class Profile extends Controller{
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
break;
case 'hot':
@@ -49,7 +49,7 @@ class Profile extends Controller{
$list = Db::name('shares')
->where('owner',$userId)
->where('type',"public")
->order('id DESC')
->order('share_time DESC')
->paginate(10);
break;
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
use think\Request;
use \app\index\model\Option;
class Queue extends Controller{
public function __construct(\think\Request $request = null){
$token = Option::getValue("task_queue_token");
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();
if(empty($tasks)){
return "none";
}else{
return json_encode($tasks);
}
}
}
@@ -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"){
return json(['error'=>1,'message'=>'您当前的无用户无法执行此操作']);
}
$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);
}
}
+16 -1
View File
@@ -132,6 +132,21 @@ class Share extends Controller{
return $shareObj->listPic($shareId,$filePath);
}
public function Thumb(){
$shareId = input('get.shareKey');
$filePath = input('get.path');
if(input("get.isImg") != "true"){
return "";
}
$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 Delete(){
$shareId = input('post.id');
$shareObj = new ShareHandler($shareId,false);
@@ -163,7 +178,7 @@ class Share extends Controller{
}
$userInfo = $this->userObj->getInfo();
$groupData = $this->userObj->getGroupData();
$list = Db::name('shares')->where('owner',$this->userObj->uid)->paginate(10);
$list = Db::name('shares')->where('owner',$this->userObj->uid)->order('share_time DESC')->paginate(30);
$listData = $list->all();
foreach ($listData as $key => $value) {
if($value["source_type"]=="file"){
+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);
+110 -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,22 @@ 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 getStatics(){
$statics["fileNum"] = Db::name('files')->count();
$statics["privateShareNum"] = Db::name('shares')->where("type","private")->count();
@@ -93,6 +105,10 @@ class AdminHandler extends Model{
return $this->saveOptions($options);
}
public function saveAria2Setting($options){
return $this->saveOptions($options);
}
public function saveMailTemplate($options){
return $this->saveOptions($options);
}
@@ -102,6 +118,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 +147,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 +173,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 +354,48 @@ 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 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 +450,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 +519,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 +564,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 +710,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>";
}
}
?>
+459
View File
@@ -0,0 +1,459 @@
<?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" => $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"])){
@unlink($quenInfo["files"][$sqlData["file_index"]]["path"]);
@self::remove_directory(dirname($quenInfo["files"][$sqlData["file_index"]]["path"]));
}
}
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);
}
}
?>
+3 -3
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{
@@ -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);
+53
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,16 @@ 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;
default:
# code...
break;
@@ -69,5 +81,46 @@ 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 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");
}
}
?>
+10 -4
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;
}
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 = 90;
$maxHeight = 39;
$changeWidth = 39*$rate;
$changeHeight = 90/$rate;
if($changeWidth>=$maxWidth){
return [(int)$changeHeight,90];
}
return [39,(int)$changeWidth];
}
/**
* 输出缩略图
*
* @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();
}
}
?>
+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/2/w/90/h/39");
}
/**
* 删除某一策略下的指定七牛文件
*
* @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();
}
}
?>
+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();
}
}
?>
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace app\index\model;
use think\Model;
use think\Db;
use Upyun\Upyun;
use Upyun\Config;
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;
}
$timeOut = Option::getValue("timeout");
return [1,\S3\S3::aws_s3_link($this->policyModel["ak"], $this->policyModel["sk"],$this->policyModel["bucketname"],"/".$this->fileModel["pre_name"],3600,$this->policyModel["op_name"])];
}
/**
* 保存文件内容
*
* @param string $content 文件内容
* @return void
*/
public function saveContent($content){
$s3 = new \S3\S3($this->policyModel["ak"], $this->policyModel["sk"],false,$this->policyModel["op_pwd"]);
$s3->setSignatureVersion('v4');
$s3->putObjectString($content, $this->policyModel["bucketname"], $this->fileModel["pre_name"]);
}
/**
* 删除某一策略下的指定文件
*
* @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");
return [1,\S3\S3::aws_s3_link($this->policyModel["ak"], $this->policyModel["sk"],$this->policyModel["bucketname"],"/".$this->fileModel["pre_name"],3600,$this->policyModel["op_name"],array(),false)];
}
/**
* 删除临时文件
*
* @param string $fname 文件名
* @param array $policy 上传策略信息
* @return boolean
*/
static function deleteS3File($fname,$policy){
$s3 = new \S3\S3($policy["ak"], $policy["sk"],false,$policy["op_pwd"]);
$s3->setSignatureVersion('v4');
return $s3->deleteObject($policy["bucketname"],$fname);
}
/**
* 签名临时URL用于Office预览
*
* @return array
*/
public function signTmpUrl(){
return $this->Preview();
}
}
?>
+14
View File
@@ -81,6 +81,20 @@ 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 checkSession($user){
if($this->lockStatus){
return [false,"会话过期,请刷新页面"];
+355
View File
@@ -0,0 +1,355 @@
<?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 Do(){
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"]);
}
}
?>
+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{
}
}
?>
?>
+100 -12
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{
@@ -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+");
@@ -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;
+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();
}
}
?>
+9 -9
View File
@@ -308,23 +308,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){
@@ -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>
+12 -2
View File
@@ -82,6 +82,16 @@
</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>
@@ -440,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>
@@ -450,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}
+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>{:$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}
@@ -928,6 +928,128 @@
</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>
@@ -45,6 +45,9 @@
<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>
@@ -82,6 +85,7 @@
{case value="upyun"}又拍云{/case}
{case value="s3"}Amazon S3{/case}
{case value="remote"}远程{/case}
{case value="onedrive"}Onedrive{/case}
{default /}其他
{/switch}
</td>
+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 -->
+6
View File
@@ -76,6 +76,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>
+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>
+81
View File
@@ -0,0 +1,81 @@
{extend name="header_home" /}
{block name="title"}离线下载管理- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/remoteDownload.js"></script>
<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-12">
<div class="panel panel-primary">
<div class="panel-heading">正在进行中 <i class="fa fa-refresh" aria-hidden="true" title="刷新数据" onclick="refresh()"></i></div>
<div class="panel-body centerTable" id="loadStatus">
更新状态数据中...
</div>
<div class="table-responsive" style="display: none">
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">下载速度</th>
<th class="centerTable">进度</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="itemContent">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">已完成 </i></div>
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
</tr>
</thead>
<tbody id="completeItemContent">
</tbody>
</table>
</div>
<div class="panel-body centerTable" id="loadButton">
<button class="btn btn-primary" id="loadFinished">点击加载已完成列表</button>
</div>
</div>
</div>
</div>
<br>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}
+81
View File
@@ -0,0 +1,81 @@
{extend name="header_home" /}
{block name="title"}离线下载管理- {$options.siteName}{/block}
{block name="content"}
<script src="/static/js/remoteDownload.js"></script>
<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-12">
<div class="panel panel-primary">
<div class="panel-heading">正在进行中 <i class="fa fa-refresh" aria-hidden="true" title="刷新数据" onclick="refresh()"></i></div>
<div class="panel-body centerTable" id="loadStatus">
更新状态数据中...
</div>
<div class="table-responsive" style="display: none">
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">下载速度</th>
<th class="centerTable">进度</th>
<th class="centerTable">操作</th>
</tr>
</thead>
<tbody id="itemContent">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">已完成 </i></div>
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th width="5%" class="centerTable">#</th>
<th width="50%" >文件名</th>
<th class="centerTable">大小</th>
<th class="centerTable">储存位置</th>
<th class="centerTable">状态</th>
</tr>
</thead>
<tbody id="completeItemContent">
</tbody>
</table>
</div>
<div class="panel-body centerTable" id="loadButton">
<button class="btn btn-primary" id="loadFinished">点击加载已完成列表</button>
</div>
</div>
</div>
</div>
<br>
</div>
</body>
<script src="/static/js/material.js"></script>
<script type="text/javascript">
upload_load=0;
</script>
{$options.js_code}
</html>
{/block}
+3
View File
@@ -3,6 +3,7 @@
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<meta name="theme-color" content="#4e64d9"/>
<title>我的文件 - {$options.siteName}</title>
<script src="/static/js/angular.min.js"></script>
<script src="/static/js/angular-translate.min.js"></script>
@@ -27,6 +28,8 @@
allowSource : "{$policyData.origin_link}",
upUrl : "{$policyData.server}",
allowShare:"{$groupData.allow_share}",
allowRemoteDownload:"{:explode(",",$groupData.aria2)[0]}",
allowTorrentDownload:"{:explode(",",$groupData.aria2)[1]}",
};
</script>
<script src="/static/js/home.js"></script>
+3 -3
View File
@@ -57,7 +57,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="loginButton">登录</button>
</form> <button class="btn btn-raised btn-primary waves-light" 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>
@@ -105,7 +105,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="regButton">注册</button> <div class="link-group">
</form> <button class="btn btn-raised btn-primary waves-light" 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>
@@ -163,7 +163,7 @@
{else/}
{/eq}
</form> <button class="btn btn-raised btn-primary" id="findMyFuckingPwd">找回密码</button> <div class="link-group">
</form> <button class="btn btn-raised btn-primary waves-light" id="findMyFuckingPwd">找回密码</button> <div class="link-group">
<a href="javascript:void" class="noWave" id="loginSwitch2" data-change="true">返回登录</a><br>
</div>
</div>
+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}
+3 -28
View File
@@ -70,34 +70,9 @@
</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>
<angular-filemanager></angular-filemanager>
</body>
<script type="text/javascript">
shareInfo={
+97 -103
View File
@@ -1,37 +1,35 @@
{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">
<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 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>
</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" 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">
@@ -47,7 +45,6 @@
<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>
@@ -55,14 +52,14 @@
<!-- 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 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 class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
@@ -71,84 +68,81 @@
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</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>
<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 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>
<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;
{$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 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>
</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}
</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}
+1 -1
View File
@@ -1 +1 @@
{"type":"","version":"1.0.2","version_id":3}
{"type":"","version":"1.1.0","version_id":5,"db_version":3}
+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');
}
}
+54 -12
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+1042 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
video{z-index:999}.file_name{white-space:nowrap}.iconset{overflow:auto;overflow-x:hidden;overflow-y:visible}.navbar .navbar-form .form-control,.navbar .navbar-form .form-group .form-control{padding:2px;margin:10px;color:#000}.form-control::-webkit-input-placeholder{color:red}.icon-black{color:#535353}.notWave{display:initial!important}.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main-h{position:relative;top:-135px;z-index:999}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.btn-primary:not(.btn-raised):not(.btn-link):focus,.btn-primary:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):hover{background-color:#0ca99a;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.btn-info:not(.btn-raised):not(.btn-link):focus,.btn-info:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):hover{background-color:#3cc2ff;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.img-circle{width:50px;display:inline-block}.bottom-width{padding:17px;padding-top:0;min-height:480px;padding-bottom:0}.btn{outline:0!important}.img-circle{width:30px}.avatar{margin-top:8px;color:#9b9b9b;text-align:right;padding-right:10px}.noWave{display:initial!important}.item-icon{margin-bottom:7px}.modal-backdrop{z-index:100}input{padding:7px 10px;width:100%;height:50px;outline:0;transition:opacity box-shadow .5s;box-shadow:0 1px 2px 0 #787878;border:0}input:focus{transition:opacity box-shadow .5s;box-shadow:0 1px 14px}h1{color:#fffb5;margin-bottom:50px}.cent{text-align:center}.table-responsive{background-color:#fff}.table{margin-bottom:0}
video{z-index:999}.file_name{white-space:nowrap}.iconset{overflow:auto;overflow-x:hidden;overflow-y:visible}.navbar .navbar-form .form-control,.navbar .navbar-form .form-group .form-control{padding:2px;margin:10px;color:#000}.form-control::-webkit-input-placeholder{color:red}.icon-black{color:#535353}.notWave{display:initial!important}.header-panel{background-color:#3f51b5;height:250px;position:relative;z-index:3}.header-panel div{position:relative;height:100%}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.navbar{margin-bottom:0}.main-h{position:relative;top:-135px;z-index:999}.jumbotron{padding:0!important;transition:all .2s linear;background-color:rgba(255,255,255,0)!important}.jumbotron:hover{box-shadow:1px 4px 8px 0 rgba(0,0,0,.2),1px 3px 6px 0 rgba(0,0,0,.19)!important;transition:box-shadow .2s}.btn-primary:not(.btn-raised):not(.btn-link):focus,.btn-primary:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-primary:not(.btn-raised):not(.btn-link):hover{background-color:#0ca99a;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.btn-info:not(.btn-raised):not(.btn-link):focus,.btn-info:not(.btn-raised):not(.btn-link):hover,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):focus,.input-group-btn .btn-info:not(.btn-raised):not(.btn-link):hover{background-color:#3cc2ff;box-shadow:5px 3px 10px 4px rgba(0,0,0,.12),0 1px 1px 0 rgba(0,0,0,.24)}.img-circle{width:50px;display:inline-block}.bottom-width{padding:17px;padding-top:0;min-height:480px;padding-bottom:0}.btn{outline:0!important}.img-circle{width:30px}.avatar{margin-top:8px;color:#9b9b9b;text-align:right;padding-right:10px}.noWave{display:initial!important}.item-icon{margin-bottom:7px}.modal-backdrop{z-index:100}input{padding:7px 10px;width:100%;height:50px;outline:0;transition:opacity box-shadow .5s;box-shadow:0 1px 2px 0 #787878;border:0}input:focus{transition:opacity box-shadow .5s;box-shadow:0 1px 14px}h1{color:#dee0f2;margin-bottom:50px}.cent{text-align:center}.table-responsive{background-color:#fff}.table{margin-bottom:0}
+1050 -1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+18
View File
@@ -56,6 +56,24 @@ $("#localPolicy").submit(function() {
});
return false;
})
$("#onedrivePolicy").submit(function() {
$("#savePolicy").attr("disabled", "true");
$.post("/Admin/SavePolicy",
$("#onedrivePolicy").serialize()
, function(data) {
if (data.error == "1") {
toastr["warning"](data.msg);
$("#savePolicy").removeAttr("disabled");
} else if (data.error == "200") {
toastr["success"]("上传策略已添加");
location.href = "/Admin/UpdateOnedriveToken?id="+data.id;
}else{
toastr["warning"]("未知错误");
$("#savePolicy").removeAttr("disabled");
}
});
return false;
})
$("#qiniuPolicy").submit(function() {
$("#saveQiniu").attr("disabled", "true");
$.post("/Admin/SavePolicy",
+34
View File
@@ -0,0 +1,34 @@
$("#saveAria2").click(function() {
$("#saveAria2").attr("disabled", "true");
$.post("/Admin/SaveAria2Setting",
$("#aria2Options").serialize()
, function(data) {
if (data.error == "1") {
toastr["warning"](data.msg);
$("#saveAria2").removeAttr("disabled");
} else if (data.error == "200") {
toastr["success"]("设置已保存");
$("#saveAria2").removeAttr("disabled");
}else{
toastr["warning"]("未知错误");
$("#saveAria2").removeAttr("disabled");
}
});
})
function cancel(id){
$.post("/Admin/CancelDownload", {id:id}, function(data){
if(data.error){
toastr["warning"](data.message);
}else{
var pid = $("#i-"+id).attr("data-pid");
$("[data-pid='"+pid+"'").remove();
toastr["success"](data.message);
}
})
}
$(document).ready(function(){
if(document.location.href.indexOf("page")!=-1){
$("[href='#list']").click();
}
})
+8 -1
View File
@@ -127,4 +127,11 @@ $("#saveYz").click(function() {
$("#saveYz").removeAttr("disabled");
}
});
})
})
$("[name='sendfile']").change(function(){
if($(this).val()=="1"){
$("#sendfile_header").slideDown();
}else{
$("#sendfile_header").slideUp();
}
});
+3 -3
View File
@@ -32,7 +32,7 @@ $("#order").children().click(function() {
$.cookie('orderMethodShare', $(this).children().attr("data-method"));
location.href = "/Admin/Shares?page=1";
})
$("[data-action='delete'").click(function() {
$("[data-action='delete']").click(function() {
var shareId = $(this).attr("data-id");
$(this).attr("disabled", "true");
var thisObj = $(this);
@@ -50,7 +50,7 @@ $("[data-action='delete'").click(function() {
}
});
})
$("[data-action='change'").click(function() {
$("[data-action='change']").click(function() {
var shareId = $(this).attr("data-id");
$(this).attr("disabled", "true");
var thisObj = $(this);
@@ -79,4 +79,4 @@ $("#delAll").click(function(){
}, function(data) {
location.href = "/Admin/Shares?page=1";
});
})
})
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
View File
@@ -49,6 +49,9 @@
changePermissions: false,
upload: false,
shareFile: uploadConfig.allowShare == "1" ? true : false,
allowRemoteDownload: uploadConfig.allowRemoteDownload,
allowTorrentDownload: uploadConfig.allowTorrentDownload,
}),
});
}]);
+8
View File
@@ -26,8 +26,13 @@ changeColor("#4e64d9","#3f51b5");
$(".captcha_img").click(function() {
$("[alt='captcha']:visible").attr('src', "/captcha");
})
function updateMetaThemeColor(themeColor) {
$('meta[name=theme-color]').remove();
$('head').append('<meta name="theme-color" content="'+themeColor+'">');
}
function switchToReg(){
changeColor("#46adff","#2196F3");
updateMetaThemeColor("#2196F3");
$("#logForm").hide();
$("#regForm").show();
$("[alt='captcha']:visible").attr('src', "/captcha");
@@ -36,6 +41,7 @@ function switchToReg(){
}
function switchToLog(){
changeColor("#4e64d9","#3f51b5");
updateMetaThemeColor("#3f51b5");
$("#regForm").hide();
$("#forgetForm").hide();
$("#logForm").show();
@@ -45,6 +51,7 @@ function switchToLog(){
}
function switchToEmail(){
changeColor("#009688","#4CAF50");
updateMetaThemeColor("#4CAF50");
$("#regForm").hide();
$("#emailCheck").show();
$("#emailCheck").removeClass("animated zoomIn");
@@ -52,6 +59,7 @@ function switchToEmail(){
}
function switchToForget(){
changeColor("#FF9800","#F44336");
updateMetaThemeColor("#F44336");
$("#regForm").hide();
$("#logForm").hide();
$("#forgetForm").show();
+5 -3
View File
@@ -183,7 +183,7 @@ function QiniuJsSDK() {
"https://up.qbox.me"
]
};
}else if(uploadConfig.saveType == "local" || uploadConfig.saveType == "oss" ||uploadConfig.saveType == "upyun"||uploadConfig.saveType == "s3"|| uploadConfig.saveType == "remote"){
}else if(uploadConfig.saveType == "local" || uploadConfig.saveType == "oss" ||uploadConfig.saveType == "upyun"||uploadConfig.saveType == "s3"|| uploadConfig.saveType == "remote"||uploadConfig.saveType == "onedrive"){
qiniuUploadUrl = uploadConfig.upUrl;
var qiniuUploadUrls = [uploadConfig.upUrl,];
var qiniuUpHosts = {
@@ -994,7 +994,7 @@ function QiniuJsSDK() {
'token': that.token,
'x:path': file.path
};
}else if(uploadConfig.saveType == "local"){
}else if(uploadConfig.saveType == "local"||uploadConfig.saveType == "onedrive"){
multipart_params_obj = {
'path': file.path
};
@@ -1493,7 +1493,7 @@ function QiniuJsSDK() {
}
}
local_path = "";
if(uploadConfig.saveType == "local"){
if(uploadConfig.saveType == "local"||uploadConfig.saveType == "onedrive"){
pathTmp = file.path;
if(file.path == ""){
pathTmp = "ROOTDIR";
@@ -1501,6 +1501,8 @@ function QiniuJsSDK() {
local_path = '/path/'+that.URLSafeBase64Encode(pathTmp);
}
if(uploadConfig.saveType == "remote"){
pathTmp = file.path;
local_path = that.URLSafeBase64Encode(pathTmp);
var url = qiniuUploadUrl + 'mkfile.php?size=' + file.size +"&key="+ key+"&fname="+ fname +"&path="+local_path;
}else{
var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url+local_path;
+137
View File
@@ -0,0 +1,137 @@
function getMemory() {
$.get("/Member/Memory", function(data) {
var dataObj = eval("(" + data + ")");
if (dataObj.rate >= 100) {
$("#memory_bar").css("width", "100%");
$("#memory_bar").addClass("progress-bar-warning");
toastr["error"]("您的已用容量已超过容量配额,请尽快删除多余文件或购买容量");
} else {
$("#memory_bar").css("width", dataObj.rate + "%");
}
$("#used").html(dataObj.used);
$("#total").html(dataObj.total);
});
}
page = 1;
window.onload = function() {
$.material.init();
getMemory();
}
$(function() {
$("#loadFinished").click(function(){
$.getJSON("/RemoteDownload/ListFinished?page="+page, function(data) {
if(data.length == 0){
$("#loadFinished").html("已加载全部");
$("#loadFinished").attr("disabled","true");
}else{
$("#loadFinished").html("继续加载");
}
data.forEach(function(e) {
$("#completeItemContent").append(function() {
var row = '<tr id="i-' + e["id"] + '" data-pid="'+e["pid"]+'"><th scope="row" class="centerTable">' + e["id"] + '</th><td>' + e["fileName"] + '</td>';
row = row + '<td class="centerTable">' + bytesToSize(e["totalLength"]) + '</td>';
row = row + '<td class="centerTable">' + e["save_dir"] + '</td>';
switch(e["status"]){
case "error":
row = row +'<td class="centerTable"><span class="download-error" data-toggle="tooltip" data-placement="top" title="'+e["msg"]+'">失败</span></td>'
break;
case "canceled":
row = row +'<td class="centerTable"><span >取消</span></td>'
break;
case "canceled":
row = row +'<td class="centerTable"><span >取消</span></td>'
break;
case "complete":
row = row +'<td class="centerTable"><span class="download-success">完成</span></td>'
break;
}
return row + "</tr>";
});
switch(e["status"]){
case "error":
$("#i-" + e["id"]).addClass("td-error");
$('[data-toggle="tooltip"]').tooltip()
break;
case "complete":
$("#i-" + e["id"]).addClass("td-success");
break;
}
});
page++;
})
})
})
$.get("/RemoteDownload/FlushUser", function() {
$("#loadStatus").html("加载下载列表中...");
loadDownloadingList();
})
function loadDownloadingList() {
$("#itemContent").html("");
$.getJSON("/RemoteDownload/ListDownloading", function(data) {
if(data.length == 0){
$("#loadStatus").html("下载列表为空");
}
data.forEach(function(e) {
$("#itemContent").append(function() {
var row = '<tr id="i-' + e["id"] + '" data-pid="'+e["pid"]+'"><th scope="row" class="centerTable">' + e["id"] + '</th><td>' + e["fileName"] + '</td>';
row = row + '<td class="centerTable">' + bytesToSize(e["totalLength"]) + '</td>';
row = row + '<td class="centerTable">' + e["save_dir"] + '</td>';
if (e["downloadSpeed"] == "0") {
row = row + '<td class="centerTable">-</td>';
} else {
row = row + '<td class="centerTable">' + bytesToSize(e["downloadSpeed"]) + '/s</td>';
}
row = row + '<td class="centerTable">' + GetPercent(e["completedLength"], e["totalLength"]) + '</td>'
row = row + '<td class="centerTable"><a href="javascript:" onclick="cancel('+e["id"]+')" >取消</a></td>'
return row + "</tr>";
});
$("#i-" + e["id"]).css({
"background-image": "-webkit-gradient(linear, left top, right top, from(#ecefff), to(white), color-stop("+e["completedLength"]/e["totalLength"]+", #ecefff), color-stop("+e["completedLength"]/e["totalLength"]+", white))",
});
$(".table-responsive").slideDown();
$("#loadStatus").slideUp();
});
})
}
function bytesToSize(bytes) {
if (bytes === 0) return '0 B';
var k = 1024, // or 1024
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
function GetPercent(num, total) {
num = parseFloat(num);
total = parseFloat(total);
if (isNaN(num) || isNaN(total)) {
return "-";
}
return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00 + "%");
}
function cancel(id){
$.post("/RemoteDownload/Cancel", {id:id}, function(data){
if(data.error){
toastr["warning"](data.message);
}else{
var pid = $("#i-"+id).attr("data-pid");
$("[data-pid='"+pid+"'").remove();
toastr["success"](data.message);
}
})
}
function refresh(){
$.get("/RemoteDownload/FlushUser?i="+Math.random(), function() {
$("#loadStatus").html("加载下载列表中...");
loadDownloadingList();
})
}
+2 -1
View File
@@ -5,7 +5,7 @@
config.set({
appName: 'angular-filemanager',
defaultLang: 'zh_cn',
sidebar: false,
sidebar: true,
pickCallback: function(item) {
var msg = 'Picked %s "%s" for external use'
.replace('%s', item.type)
@@ -18,6 +18,7 @@
changePermissions: false,
upload: false,
shareFile: false,
shareFile: false,
}),
});
}]);
+11 -22
View File
@@ -25,25 +25,10 @@
}
return parseInt(size * 100) / 100 + filetype[i];
}
Date.prototype.format = function(fmt) {
var o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3) / 3),
"S": this.getMilliseconds()
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
function formateDate(dateString){
var bigDate = dateString.split(" ");
var smallDate = bigDate[0].split("-");
return smallDate[0]+"年"+smallDate[1]+"月"+smallDate[2]+"日 "+bigDate[1];
}
function audioPause() {
@@ -53,7 +38,7 @@
$("#size").html(getSize(shareInfo.fileSize));
$("#down_num").html(shareInfo.downloadNum);
$("#view_num").html(shareInfo.ViewNum);
shareTime = new Date(shareInfo.shareDate).format("yyyy年MM月dd日 hh:mm");
shareTime = formateDate(shareInfo.shareDate);
$("#share_time").html(shareTime);
$("#download").click(function() {
$.post("/Share/getDownloadUrl", {
@@ -95,6 +80,10 @@
video: {
url: url
},
danmaku: {
id: 'Cloudreve_'+shareInfo.shareId,
api: 'https://api.prprpr.me/dplayer/'
}
});
dp.on("fullscreen", function(){
$(".modal-backdrop").hide();
@@ -148,9 +137,9 @@
$(".file-sign").html('<i class="fa fa-file-movie-o" aria-hidden="true"></i>')
$("#previewButton").click(function() {
if (shareInfo.allowPreview) {
$(".previewContent").html('<div id="preview-target" style="width: 100%;object-fit: fill" class="preview"></div>');
$('#previewModal').modal();
$(".previewContent").html('<div id="preview-target" style="width: 100%;object-fit: fill;height:400px" class="preview"></div>');
loadDPlayer('/Share/Preview/' + shareInfo.shareId);
$('#previewModal').modal();
} else {
toastr["error"]("请先登录")
}
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
// 定义项目路径
define('APP_PATH', __DIR__ . '/application/');
// 加载框架引导文件
require __DIR__.'/thinkphp/console.php';
+3 -1
View File
@@ -361,7 +361,9 @@
<?php } else { ?>
<div class="exception">
<div class="info"><h1><?php echo htmlentities($message); ?></h1></div>
<div class="info"><h1><?php echo htmlentities($message); ?></h1>
<p>我们都有不顺利的时候,请坐和放宽~</p>
</div>
</div>
<?php } ?>