mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-11 18:56:21 +00:00
added admin panel with stats and logs
This commit is contained in:
35
DEV.md
Normal file
35
DEV.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Folder structure
|
||||
All uploads will be created as folders with the name of the file in the `./data` directory.
|
||||
|
||||
```
|
||||
data/
|
||||
data/<file_name>/
|
||||
data/<file_name>/<file_name> # the actual file eg abc1
|
||||
data/<file_name>/meta.json # file containing the metadata
|
||||
```
|
||||
|
||||
## Metadata file
|
||||
The metadata file will contain data like in the following example:
|
||||
|
||||
```json
|
||||
{
|
||||
"mime": "image\/jpeg",
|
||||
"size": 504921,
|
||||
"size_human": "493.09 KB",
|
||||
"original_filename": "PXL_20250511_122019042-POP_OUT.jpg",
|
||||
"hash": "0gfmeo.jpg",
|
||||
"sha1": "7dc14ad3f0c273ce0188aeef19a6104e81ba67dd",
|
||||
"uploaded": 1747557347,
|
||||
"ip": "::1",
|
||||
"useragent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/136.0.0.0 Safari\/537.36",
|
||||
"delete_code": "z94fd5tyto1u2bww23kec0f52irb7x49",
|
||||
"delete_url": "http:\/\/localhost:8080\/delete_z94fd5tyto1u2bww23kec0f52irb7x49\/0gfmeo.jpg",
|
||||
"remote_port": "35856"
|
||||
}
|
||||
```
|
||||
|
||||
# Redis index
|
||||
If redis caching is enabled, this structure will be created on the fly:
|
||||
|
||||
- cache:byurl:<url> => <content controller>;<file_name> # cached response
|
||||
- served:<file_name> => number of views # view count
|
||||
3
TODO.md
3
TODO.md
@@ -7,4 +7,5 @@
|
||||
- [ ] Add a way for users to report content
|
||||
- [ ] Review reports
|
||||
- [ ] Delete content if needed in a way that the user can't reupload it
|
||||
- [ ] Add a way to delete content that is older than X days
|
||||
- [ ] Add a way to delete content that is older than X days
|
||||
- [ ] Bruteforce protection for admin login
|
||||
|
||||
@@ -3,12 +3,11 @@ services:
|
||||
pictshare:
|
||||
image: 'HaschekSolutions/pictshare:3'
|
||||
environment:
|
||||
- SERVER_NAME=:80 # required by caddy
|
||||
- URL=http://localhost:8080/
|
||||
- MAX_UPLOAD_SIZE=20 #in MB
|
||||
# Security settings
|
||||
- ADMIN_PASSWORD= # password for the admin panel. if empty, admin panel is disabled
|
||||
- ALLOWED_SUBNET= #IP address or subnet mask to allow upload to the server
|
||||
- ALLOWED_SUBNET= #IP address or subnet mask to allow upload and access to the admin panel
|
||||
- CONTENTCONTROLLERS= # limit uploaded file types
|
||||
- MASTER_DELETE_CODE= # code to delete all files
|
||||
- MASTER_DELETE_IP= # IP address to delete all files even without delete code
|
||||
@@ -43,6 +42,7 @@ services:
|
||||
- REDIS_CACHING=true
|
||||
- REDIS_SERVER=/run/redis/redis.sock
|
||||
- REDIS_PORT=0
|
||||
- SERVER_NAME=:80 # required by caddy
|
||||
ports:
|
||||
- 8080:80
|
||||
volumes:
|
||||
|
||||
102
src/inc/core.php
102
src/inc/core.php
@@ -29,26 +29,54 @@ function architect($u)
|
||||
{
|
||||
$forbidden = true;
|
||||
}
|
||||
return renderTemplate('main.html.php',['forbidden'=>$forbidden]);
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('main.html.php',['forbidden'=>$forbidden])]);
|
||||
}
|
||||
|
||||
// admin logic
|
||||
if($u[0] == 'admin' && defined('ADMIN_PASSWORD') && ADMIN_PASSWORD != '')
|
||||
{
|
||||
// block checks
|
||||
if (defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange(getUserIP(), ALLOWED_SUBNET))
|
||||
return;
|
||||
else if (defined('MASTER_DELETE_IP') && MASTER_DELETE_IP != '' && !isIPInRange(getUserIP(), MASTER_DELETE_IP))
|
||||
return;
|
||||
session_start();
|
||||
if($_REQUEST['password'] && $_REQUEST['password']== ADMIN_PASSWORD)
|
||||
{
|
||||
$_SESSION['admin'] = true;
|
||||
switch($u[1]){
|
||||
case 'stats':
|
||||
if(!$_SESSION['admin'])
|
||||
header('Location: /admin');
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.stats.html.php',['stats'=>getStats()])]);
|
||||
case 'logs':
|
||||
if(!$_SESSION['admin'])
|
||||
header('Location: /admin');
|
||||
switch($u[2])
|
||||
{
|
||||
case 'app':
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'app','logs'=>getLogs('app',$u[3])])]);
|
||||
case 'error':
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'error','logs'=>getLogs('error',$u[3])])]);
|
||||
case 'views':
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs-table.html.php',['type'=>'views','logs'=>getLogs('views',$u[3])])]);
|
||||
default:
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.logs.html.php')]);
|
||||
}
|
||||
default:
|
||||
if($_REQUEST['password'] && $_REQUEST['password']== ADMIN_PASSWORD)
|
||||
{
|
||||
$_SESSION['admin'] = true;
|
||||
}
|
||||
if($_SESSION['admin'])
|
||||
{
|
||||
if(isset($_REQUEST['logout']))
|
||||
{
|
||||
unset($_SESSION['admin']);
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
return renderTemplate('index.html.php',['main'=>renderTemplate('admin.html.php')]);
|
||||
}
|
||||
if($_SESSION['admin'])
|
||||
{
|
||||
if(isset($_REQUEST['logout']))
|
||||
{
|
||||
unset($_SESSION['admin']);
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
return renderTemplate('admin.html.php');
|
||||
|
||||
|
||||
}
|
||||
|
||||
//check cache
|
||||
@@ -59,7 +87,7 @@ function architect($u)
|
||||
{
|
||||
list($cc, $hash) = explode(';', $cache_data);
|
||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||
addToLog("Cache hit: ".getUserIP()." viewed $hash\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
||||
addToLog(getUserIP()."\tviewed\t$hash\tFrom cache. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||
$GLOBALS['redis']->incr("served:$hash");
|
||||
return (new $cc())->handleHash($hash,$u);
|
||||
}
|
||||
@@ -73,7 +101,7 @@ function architect($u)
|
||||
{
|
||||
$hash = $el;
|
||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||
addToLog(getUserIP()." viewed $hash\tIt was locally found\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
||||
addToLog(getUserIP()."\tviewed\t$hash\tIt was locally found. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||
break;
|
||||
}
|
||||
// if we don't have a hash yet but the element looks like it could be a hash
|
||||
@@ -93,7 +121,7 @@ function architect($u)
|
||||
storeFile(ROOT.DS.'tmp'.DS.$hash,$hash,true);
|
||||
|
||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||
addToLog(getUserIP()." viewed $hash\tIt was found in Storage Controller $contr\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
||||
addToLog(getUserIP()."\tviewed\t$hash\tIt was found in Storage Controller $contr. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||
|
||||
break; // we break here because we already have the file. no need to check other storage controllers
|
||||
}
|
||||
@@ -110,7 +138,7 @@ function architect($u)
|
||||
unlink(ROOT.DS.'tmp'.DS.$el.'.enc');
|
||||
|
||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||
addToLog(getUserIP()." viewed $hash\tIt was found encrypted in Storage Controller $contr\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
||||
addToLog(getUserIP()."\tviewed\t$hash\tIt was found encrypted in Storage Controller $contr. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||
|
||||
break; // we break here because we already have the file. no need to check other storage controllers
|
||||
}
|
||||
@@ -125,7 +153,7 @@ function architect($u)
|
||||
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
||||
{
|
||||
if(defined('LOG_VIEWS') && LOG_VIEWS===true)
|
||||
addToLog(getUserIP()." requested ".implode("/",$u)."\tIt's a dynamic image handled by $cc\t".$_SERVER['HTTP_USER_AGENT'], ROOT.DS.'logs/views.log');
|
||||
addToLog(getUserIP()." requested ".implode("/",$u)."\tIt's a dynamic image handled by $cc. Agent:\t".$_SERVER['HTTP_USER_AGENT']."\tref:\t".$_SERVER['HTTP_REFERER'], ROOT.DS.'logs/views.log');
|
||||
$hash = true;
|
||||
break;
|
||||
}
|
||||
@@ -833,7 +861,7 @@ function getDeleteCodeOfHash($hash)
|
||||
|
||||
function getMetadataOfHash($hash)
|
||||
{
|
||||
$metadata = array();
|
||||
$metadata = [];
|
||||
$metadatafile = getDataDir().DS.$hash.DS.'meta.json';
|
||||
if(file_exists($metadatafile))
|
||||
{
|
||||
@@ -1107,4 +1135,40 @@ function addToLog($data,$logfile=ROOT.DS.'logs/app.log')
|
||||
$fp = fopen($logfile,'a');
|
||||
fwrite($fp,date("d.m.y H:i")."\t[".getURL()."] | ".$data."\n");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
function getStats(){
|
||||
$stats = array();
|
||||
$stats['total_files'] = 0;
|
||||
$stats['total_size'] = 0;
|
||||
$stats['total_files'] = count(glob(getDataDir().DS.'*', GLOB_ONLYDIR));
|
||||
foreach (glob(getDataDir().DS.'*') as $dir) {
|
||||
if (is_dir($dir)) {
|
||||
$stats['hashes'][basename($dir)] = [
|
||||
'size' => filesize($dir.DS.basename($dir)),
|
||||
'files' => count(glob($dir.DS.'*')),
|
||||
'views' => $GLOBALS['redis']->get('served:'.basename($dir)),
|
||||
'metadata' => getMetadataOfHash(basename($dir)),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
|
||||
function getLogs($type='app',$filter=false)
|
||||
{
|
||||
$logs = array();
|
||||
$logfile = ROOT.DS.'logs/'.$type.'.log';
|
||||
if($type && file_exists($logfile))
|
||||
{
|
||||
$handle = fopen($logfile, "r");
|
||||
if ($handle) {
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
if($filter && strpos($line,$filter)===false) continue;
|
||||
$logs[] = $line;
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
return $logs;
|
||||
}
|
||||
@@ -1,93 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
||||
<html class="no-js"> <!--<![endif]-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PictShare - the smart CDN</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- PictShare overwrites -->
|
||||
<link href="/css/pictshare.css" rel="stylesheet">
|
||||
<link href="/css/dropzone.css" rel="stylesheet">
|
||||
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- github-fork-ribbon-css
|
||||
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
||||
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script>
|
||||
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
||||
</script>
|
||||
|
||||
<meta name="description" content="Free image sharing, linking and tracking">
|
||||
<meta name="keywords" content="image, share, hosting, free">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="copyright" content="Haschek Solutions">
|
||||
<meta name="language" content="EN,DE">
|
||||
<meta name="author" content="Haschek Solutions">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="rating" content="general">
|
||||
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
|
||||
<div class="container" id="headcontainer">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
||||
</div>
|
||||
<h2>Admin Panel</h2>
|
||||
<?php if (!$_SESSION['admin']) { ?>
|
||||
<form method="post" action="/admin">
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" aria-label="Password" aria-describedby="btn-addn">
|
||||
<button class="btn btn-outline-secondary" type="submit" id="btn-addn">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
<?php if ($_SESSION['admin']) { ?>
|
||||
<div class="alert alert-success" role="alert">You are logged in as admin</div>
|
||||
<form method="post" action="/admin">
|
||||
<button type="submit" name="logout" class="btn btn-danger">Logout</button>
|
||||
</form>
|
||||
|
||||
<div class="container">
|
||||
<h2>Admin Panel</h2>
|
||||
<?php if (!$_SESSION['admin']) { ?>
|
||||
<form method="post" action="/admin">
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" class="form-control" name="password" placeholder="Password" aria-label="Password" aria-describedby="btn-addn">
|
||||
<button class="btn btn-outline-secondary" type="submit" id="btn-addn">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
<?php if ($_SESSION['admin']) { ?>
|
||||
<div class="alert alert-success" role="alert">You are logged in as admin</div>
|
||||
<form method="post" action="/admin">
|
||||
<button type="submit" name="logout" class="btn btn-danger">Logout</button>
|
||||
</form>
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/admin/stats">Stats</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/logs">Logs</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="#">Stats</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Review</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<footer class="text-center">(c)<?php echo date("y"); ?> by<br /><a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></footer>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/js/pictshare.js"></script>
|
||||
</BODY>
|
||||
|
||||
</HTML>
|
||||
<?php } ?>
|
||||
15
src/templates/admin.logs-table.html.php
Normal file
15
src/templates/admin.logs-table.html.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||
<li class="breadcrumb-item"><a href="/admin/logs">Logs</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page"><?=$type?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<h1><?= ucfirst($type) ?></h1>
|
||||
|
||||
<pre>
|
||||
<code><?php foreach($logs as $log):
|
||||
echo $log;
|
||||
endforeach; ?></code>
|
||||
</pre>
|
||||
10
src/templates/admin.logs.html.php
Normal file
10
src/templates/admin.logs.html.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Logs</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<a class="btn btn-primary" href="/admin/logs/app">General App logs</a>
|
||||
<a class="btn btn-primary" href="/admin/logs/views">Views</a>
|
||||
<a class="btn btn-primary" href="/admin/logs/error">Errors</a>
|
||||
41
src/templates/admin.stats.html.php
Normal file
41
src/templates/admin.stats.html.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/admin">Admin</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Stats</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<h1>Stats</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover" data-toggle="table" data-search="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sortable="true">Hash</th>
|
||||
<th scope="col" data-sortable="true">Views</th>
|
||||
<th scope="col" data-sortable="true">Original Filename</th>
|
||||
<th scope="col" data-sortable="true">MIME type</th>
|
||||
<th scope="col" data-sortable="true">Created at</th>
|
||||
<th scope="col" data-sortable="true">Uploader IP</th>
|
||||
<?php if(defined('LOG_VIEWS') && LOG_VIEWS==true): ?>
|
||||
<th scope="col" data-sortable="true">List views</th>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($stats['hashes'] as $hash => $data): ?>
|
||||
<tr>
|
||||
<th scope="row"><a href="/<?=$hash?>"><?=$hash?></a></th>
|
||||
<td><?=$data['views']?:0?></td>
|
||||
<td><?=$data['metadata']['original_filename']?></td>
|
||||
<td><?=$data['metadata']['mime']?></td>
|
||||
<td><?=date("Y-m-d H:i",$data['metadata']['uploaded'])?></td>
|
||||
<td><?=$data['metadata']['ip']?></td>
|
||||
<?php if(defined('LOG_VIEWS') && LOG_VIEWS==true): ?>
|
||||
<td scope="col" data-sortable="true"><a class="btn btn-secondary" href="/admin/logs/views/<?=$hash?>">View logs</a></td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
75
src/templates/index.html.php
Normal file
75
src/templates/index.html.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js"> <!--<![endif]-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PictShare - the smart CDN</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- PictShare overwrites -->
|
||||
<link href="/css/pictshare.css" rel="stylesheet">
|
||||
<link href="/css/dropzone.css" rel="stylesheet">
|
||||
<link href="/css/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- github-fork-ribbon-css
|
||||
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
||||
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script>
|
||||
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
||||
</script>
|
||||
|
||||
<meta name="description" content="Free image sharing, linking and tracking">
|
||||
<meta name="keywords" content="image, share, hosting, free">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="copyright" content="Haschek Solutions">
|
||||
<meta name="language" content="EN,DE">
|
||||
<meta name="author" content="Haschek Solutions">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="rating" content="general">
|
||||
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
|
||||
<div class="container" id="headcontainer">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="main" class="container hv-100">
|
||||
<?=$main;?>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="container text-center">
|
||||
<p>created by <a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/js/jquery.min.js"></script>
|
||||
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/js/bootstrap-table.min.js"></script>
|
||||
<script src="/js/dropzone.js"></script>
|
||||
<script src="/js/highlight.pack.js"></script>
|
||||
<script src="/js/pictshare.js"></script>
|
||||
</BODY>
|
||||
|
||||
</HTML>
|
||||
@@ -1,161 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
|
||||
<!--[if (gt IEMobile 7)|!(IEMobile)]><!-->
|
||||
<html class="no-js"> <!--<![endif]-->
|
||||
<?php
|
||||
if (file_exists(ROOT . DS . 'notice.txt'))
|
||||
echo '<div class="alert alert-warning" role="alert">' . file_get_contents(ROOT . DS . 'notice.txt') . '</div>';
|
||||
?>
|
||||
<div class="well">
|
||||
<?php if ($forbidden === true) { ?>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PictShare - the smart CDN</title>
|
||||
<h2>Upload forbidden</h2>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||||
<p>Due to configured restrictions, you are not allowed to upload files at this time</p>
|
||||
|
||||
<!-- PictShare overwrites -->
|
||||
<link href="/css/pictshare.css" rel="stylesheet">
|
||||
<link href="/css/dropzone.css" rel="stylesheet">
|
||||
<link href="/css/hljs-dracula.css" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- github-fork-ribbon-css
|
||||
https://simonwhitaker.github.io/github-fork-ribbon-css/ -->
|
||||
<link href="/css/gh-fork-ribbon.min.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script>
|
||||
var maxUploadFileSize = <?php echo (int)(ini_get('upload_max_filesize')); ?>
|
||||
</script>
|
||||
|
||||
<meta name="description" content="Free image sharing, linking and tracking">
|
||||
<meta name="keywords" content="image, share, hosting, free">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="copyright" content="Haschek Solutions">
|
||||
<meta name="language" content="EN,DE">
|
||||
<meta name="author" content="Haschek Solutions">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="rating" content="general">
|
||||
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<a class="github-fork-ribbon left-top" href="https://github.com/HaschekSolutions/pictshare" data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
|
||||
<div class="container" id="headcontainer">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<a href="/"><img src="/css/imgs/logo/horizontalv3.png" /></a>
|
||||
<?php
|
||||
if (file_exists(ROOT . DS . 'notice.txt'))
|
||||
echo '<div class="alert alert-warning" role="alert">' . file_get_contents(ROOT . DS . 'notice.txt') . '</div>';
|
||||
?>
|
||||
<div class="well">
|
||||
<?php if ($forbidden === true) { ?>
|
||||
|
||||
<h2>Upload forbidden</h2>
|
||||
|
||||
<p>Due to configured restrictions, you are not allowed to upload files at this time</p>
|
||||
|
||||
<?php } else { ?>
|
||||
<div id="uploadinfo"></div>
|
||||
<p>
|
||||
Max Upload size: <?=(int)(ini_get('upload_max_filesize'))?>MB / File<br/>
|
||||
Allowed file types: <?= implode(', ', getAllContentFiletypes()) ?>
|
||||
<?php
|
||||
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''):?>
|
||||
<br>Upload Code: <input type="password" id="uploadcode" />
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<form class="dropzone well" id="dropzone" method="post" action="/api/upload" enctype="multipart/form-data">
|
||||
<div class="fallback">
|
||||
<input name="file" type="file" multiple />
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div id="uploadinfo"></div>
|
||||
<p>
|
||||
Max Upload size: <?= (int)(ini_get('upload_max_filesize')) ?>MB / File<br />
|
||||
Allowed file types: <?= implode(', ', getAllContentFiletypes()) ?>
|
||||
<?php
|
||||
if (defined('UPLOAD_CODE') && UPLOAD_CODE != ''): ?>
|
||||
<br>Upload Code: <input type="password" id="uploadcode" />
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<form class="dropzone well" id="dropzone" method="post" action="/api/upload" enctype="multipart/form-data">
|
||||
<div class="fallback">
|
||||
<input name="file" type="file" multiple />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 id="api" class="section-heading">Using PictShare</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Basics</h3>
|
||||
<p>
|
||||
When you upload an image you'll get a link like this:
|
||||
<pre><code class="url"><?= getURL() ?>abcef123.jpg</code></pre>
|
||||
You can modify the size of the image by adding a size parameter to the URL. For example this will render the image in 800x600:
|
||||
<pre><code class="url"><?= getURL() ?>800x600/abcef123.jpg</code></pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to force the size to really be 800x600, you can use the "forcesize" parameter. It will still keep the aspect ratio but zoom in as needed to fit the dimensions
|
||||
<pre><code class="url"><?= getURL() ?>800x600/forcesize/abcef123.jpg</code></pre>
|
||||
</p>
|
||||
|
||||
<p>There are many more of these <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/MODIFIERS.md">modifiers</a> and even <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md">filters</a> available</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="no-print border border-primary border-2 opacity-50">
|
||||
|
||||
<h2 id="api" class="section-heading">Using the API</h2>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h2>Basics</h2>
|
||||
|
||||
<p>
|
||||
All API calls are done via GET or POST requests. The API will return JSON encoded data.
|
||||
</p>
|
||||
|
||||
Base URL
|
||||
<pre><code class="url"><?= getURL() ?>api</code></pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2 id="api" class="section-heading">Using PictShare</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h3>Basics</h3>
|
||||
<p>
|
||||
When you upload an image you'll get a link like this:<pre><code class="url"><?= getURL() ?>abcef123.jpg</code></pre>
|
||||
You can modify the size of the image by adding a size parameter to the URL. For example this will render the image in 800x600:
|
||||
<pre><code class="url"><?= getURL() ?>800x600/abcef123.jpg</code></pre>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to force the size to really be 800x600, you can use the "forcesize" parameter. It will still keep the aspect ratio but zoom in as needed to fit the dimensions
|
||||
<pre><code class="url"><?= getURL() ?>800x600/forcesize/abcef123.jpg</code></pre>
|
||||
</p>
|
||||
|
||||
<p>There are many more of these <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/MODIFIERS.md">modifiers</a> and even <a target="_blank" href="https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md">filters</a> available</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="no-print border border-primary border-2 opacity-50">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h2 id="api" class="section-heading">Using the API</h2>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h2>Basics</h2>
|
||||
|
||||
<p>
|
||||
All API calls are done via GET or POST requests. The API will return JSON encoded data.
|
||||
</p>
|
||||
|
||||
Base URL
|
||||
<pre><code class="url"><?= getURL() ?>api</code></pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<h2>Error handling</h2>
|
||||
When the status is err there will always be a field "reason" that explains what went wrong.
|
||||
<pre><code class="json">
|
||||
<div class="col-6">
|
||||
<h2>Error handling</h2>
|
||||
When the status is err there will always be a field "reason" that explains what went wrong.
|
||||
<pre><code class="json">
|
||||
{
|
||||
"status": "err",
|
||||
"reason": "File not a valid image"
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-100">
|
||||
<hr />
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<h2>Uploading an image</h2>
|
||||
<div class="col-6">
|
||||
<h2>Uploading an image</h2>
|
||||
|
||||
API call
|
||||
<pre><code class="url">/upload</code></pre>
|
||||
API call
|
||||
<pre><code class="url">/upload</code></pre>
|
||||
|
||||
<p>You can post a file using the POST variable <span class="badge text-bg-secondary">file</span></p>
|
||||
<p>You can post a file using the POST variable <span class="badge text-bg-secondary">file</span></p>
|
||||
|
||||
CURL example
|
||||
<pre><code class="bash">curl -s -F "file=@myphoto.jpg" "<?= getURL() ?>upload"</code></pre>
|
||||
CURL example
|
||||
<pre><code class="bash">curl -s -F "file=@myphoto.jpg" "<?= getURL() ?>upload"</code></pre>
|
||||
|
||||
Output
|
||||
<pre><code class="json">
|
||||
Output
|
||||
<pre><code class="json">
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "7eli4d.jpg",
|
||||
@@ -164,21 +103,21 @@
|
||||
"delete_code": "jxgat3wze8lmn9sqwxy4x32p2xm7211g",
|
||||
"delete_url": "http://localhost:8080/delete_jxgat3wze8lmn9sqwxy4x32p2xm7211g/7eli4d.jpg"
|
||||
}</code></pre>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2>Grabbing a URL</h2>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2>Grabbing a URL</h2>
|
||||
|
||||
API call
|
||||
<pre><code class="url">/upload?url=<span class="badge text-bg-secondary">url of image</span></code></pre>
|
||||
API call
|
||||
<pre><code class="url">/upload?url=<span class="badge text-bg-secondary">url of image</span></code></pre>
|
||||
|
||||
<p>You can specify a URL in the POST or GET variable <span class="badge text-bg-secondary">url</span>
|
||||
that the call will try to download and process.</p>
|
||||
<p>You can specify a URL in the POST or GET variable <span class="badge text-bg-secondary">url</span>
|
||||
that the call will try to download and process.</p>
|
||||
|
||||
CURL example
|
||||
<pre><code class="bash">curl -s "<?= getURL() ?>api/upload?url=https://pictshare.net/d2j1e1.png</code></pre>
|
||||
CURL example
|
||||
<pre><code class="bash">curl -s "<?= getURL() ?>api/upload?url=https://pictshare.net/d2j1e1.png</code></pre>
|
||||
|
||||
Output
|
||||
<pre><code class="json">
|
||||
Output
|
||||
<pre><code class="json">
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "ysj455.webp",
|
||||
@@ -188,24 +127,24 @@
|
||||
"delete_url": "http://localhost:8080/delete_4l0w04l4s42xddt2s5mrj1wikxz11l5z/ysj455.webp"
|
||||
}
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-100">
|
||||
<hr />
|
||||
</div>
|
||||
<div class="w-100">
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<h2>Uploading Base64 encoded content</h2>
|
||||
<p>It's also possible to upload supported files via Base64 strings by providing the <span class="badge text-bg-secondary">base64</span> http parameter</p>
|
||||
<div class="col-6">
|
||||
<h2>Uploading Base64 encoded content</h2>
|
||||
<p>It's also possible to upload supported files via Base64 strings by providing the <span class="badge text-bg-secondary">base64</span> http parameter</p>
|
||||
|
||||
API call
|
||||
<pre><code class="url">/upload/?base64=<span class="badge text-bg-secondary">base64 encoded string</span></code></pre>
|
||||
API call
|
||||
<pre><code class="url">/upload/?base64=<span class="badge text-bg-secondary">base64 encoded string</span></code></pre>
|
||||
|
||||
CURL example
|
||||
<pre><code class="bash">(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 Screenshot3.jpg)") | curl --data @- <?= getURL() ?>api/upload</code></pre>
|
||||
CURL example
|
||||
<pre><code class="bash">(echo -n "base64="; echo -n "data:image/jpeg;base64,$(base64 -w 0 Screenshot3.jpg)") | curl --data @- <?= getURL() ?>api/upload</code></pre>
|
||||
|
||||
Output
|
||||
<pre><code class="json">
|
||||
Output
|
||||
<pre><code class="json">
|
||||
{
|
||||
"status": "ok",
|
||||
"hash": "5e6alk.jpg",
|
||||
@@ -215,23 +154,9 @@
|
||||
"delete_url": "http://localhost:8080/delete_7ha2b5ccvsuvdd3qdnegzb2zqa9zxb5t/5e6alk.jpg"
|
||||
}</code></pre>
|
||||
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<footer>(c)<?php echo date("y"); ?> by<br /><a href="https://haschek.solutions" target="_blank"><img height="30" src="/css/imgs/hs_logo.png" /></a></footer>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/js/dropzone.js"></script>
|
||||
<script src="/js/highlight.pack.js"></script>
|
||||
<script src="/js/pictshare.js"></script>
|
||||
</BODY>
|
||||
|
||||
</HTML>
|
||||
</div>
|
||||
10
web/css/bootstrap-table.min.css
vendored
Normal file
10
web/css/bootstrap-table.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ body {
|
||||
background-image: url('imgs/bg.png');
|
||||
background-repeat: no-repeat;
|
||||
background-color: #fff;
|
||||
padding-bottom: 60px; /* Space for the footer */
|
||||
}
|
||||
|
||||
#headcontainer
|
||||
@@ -32,4 +33,13 @@ body {
|
||||
.box__success,
|
||||
.box__error {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px; /* Set the fixed height of the footer here */
|
||||
line-height: 60px; /* Vertically center the text there */
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
10
web/js/bootstrap-table.min.js
vendored
Normal file
10
web/js/bootstrap-table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
web/js/bootstrap.bundle.min.js
vendored
Normal file
7
web/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
web/js/jquery.min.js
vendored
Normal file
2
web/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2,90 +2,92 @@ Dropzone.autoDiscover = false;
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var myDropzone = new Dropzone("#dropzone");
|
||||
if (typeof maxUploadFileSize !== "undefined")
|
||||
myDropzone.options.maxFilesize = maxUploadFileSize;
|
||||
if (document.getElementById("dropzone") != null) {
|
||||
var myDropzone = new Dropzone("#dropzone");
|
||||
if (typeof maxUploadFileSize !== "undefined")
|
||||
myDropzone.options.maxFilesize = maxUploadFileSize;
|
||||
|
||||
myDropzone.options.timeout = 0;
|
||||
myDropzone.options.timeout = 0;
|
||||
|
||||
myDropzone.on("sending", function(file, xhr, formData) {
|
||||
var uploadCodeElem = document.getElementById("uploadcode");
|
||||
if (uploadCodeElem)
|
||||
formData.append("uploadcode", uploadCodeElem.value);
|
||||
});
|
||||
myDropzone.on("sending", function (file, xhr, formData) {
|
||||
var uploadCodeElem = document.getElementById("uploadcode");
|
||||
if (uploadCodeElem)
|
||||
formData.append("uploadcode", uploadCodeElem.value);
|
||||
});
|
||||
|
||||
myDropzone.on('error', function(file, response) {
|
||||
var uploadInfo = document.getElementById("uploadinfo");
|
||||
if (response == null || response == "null") {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason is unknown :(",
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (response.status == 'err') {
|
||||
myDropzone.on('error', function (file, response) {
|
||||
var uploadInfo = document.getElementById("uploadinfo");
|
||||
if (response == null || response == "null") {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason: " + response.reason,
|
||||
"Reason is unknown :(",
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (response.status == 'err') {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason: " + response.reason,
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason: " + response,
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myDropzone.on("success", function (file, response) {
|
||||
console.log("raw response: " + response);
|
||||
var uploadInfo = document.getElementById("uploadinfo");
|
||||
if (response == null || response == "null") {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason: " + response,
|
||||
"Reason is unknown :(",
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (response.status == 'ok') {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(file.name + " uploaded as <a target='_blank' href='/" + response.hash + "'>" + response.hash + "</a>", "URL: <a target='_blank' href='" + response.url + "'>" + response.url + "</a> <button class='btn btn-primary btn-sm' onClick='navigator.clipboard.writeText(\"" + response.url + "\");'>Copy URL</button>", "success")
|
||||
);
|
||||
} else if (response.status == 'err') {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
response.reason,
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
myDropzone.on("success", function (file, response) {
|
||||
console.log("raw response: " + response);
|
||||
var uploadInfo = document.getElementById("uploadinfo");
|
||||
if (response == null || response == "null") {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
"Reason is unknown :(",
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (response.status == 'ok') {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(file.name+" uploaded as <a target='_blank' href='/" + response.hash + "'>" + response.hash + "</a>","URL: <a target='_blank' href='" + response.url + "'>" + response.url + "</a> <button class='btn btn-primary btn-sm' onClick='navigator.clipboard.writeText(\"" + response.url + "\");'>Copy URL</button>","success")
|
||||
);
|
||||
} else if (response.status == 'err') {
|
||||
uploadInfo.insertAdjacentHTML("beforeend",
|
||||
renderMessage(
|
||||
"Error uploading " + file.name,
|
||||
response.reason,
|
||||
"danger"
|
||||
)
|
||||
);
|
||||
document.onpaste = function (event) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||
for (var index in items) {
|
||||
var item = items[index];
|
||||
if (item.kind === 'file') {
|
||||
myDropzone.addFile(item.getAsFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.onpaste = function (event) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||
for (var index in items) {
|
||||
var item = items[index];
|
||||
if (item.kind === 'file') {
|
||||
myDropzone.addFile(item.getAsFile());
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function renderMessage(title,message,type){
|
||||
if(!type)
|
||||
function renderMessage(title, message, type) {
|
||||
if (!type)
|
||||
type = "danger";
|
||||
return `<div class='alert alert-${type}' role='alert'><strong>${title}</strong><br/>${message}</div>`;
|
||||
}
|
||||
Reference in New Issue
Block a user