From d3eaf27bdc7add58002aa3109dbb9c6bdd5d9bfe Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Fri, 25 Oct 2013 09:40:33 +0200 Subject: [PATCH] Closed #36 --- public_html/media/css/tabs.css | 26 +++++++ public_html/media/css/upload.css | 15 ++++ public_html/media/css/user-view.css | 27 ------- public_html/media/js/upload.js | 112 ++++++++++++++++++---------- src/Controllers/PostController.php | 66 +++++++++++++--- src/Controllers/UserController.php | 1 + src/Helpers/TextHelper.php | 22 ++++++ src/Views/post-upload.phtml | 39 ++++++++-- src/Views/post-view.phtml | 4 +- 9 files changed, 230 insertions(+), 82 deletions(-) create mode 100644 public_html/media/css/tabs.css diff --git a/public_html/media/css/tabs.css b/public_html/media/css/tabs.css new file mode 100644 index 00000000..2a029b20 --- /dev/null +++ b/public_html/media/css/tabs.css @@ -0,0 +1,26 @@ +.tabs ul { + list-style-type: none; + margin: 0 0 1em 0; + padding: 0; + border-bottom: 1px solid #ccc; +} +.tabs li { + display: inline-block; +} + +.tabs li a { + display: inline-block; + padding: 0.5em 1em; + margin-bottom: -1px; +} + +.tabs li a { + border: 1px solid white; + border-bottom: 1px solid #ccc; + color: silver; +} +.tabs li.selected a { + border: 1px solid #ccc; + border-bottom: 1px solid white; + color: inherit; +} diff --git a/public_html/media/css/upload.css b/public_html/media/css/upload.css index fdcf7a0f..d83bfa26 100644 --- a/public_html/media/css/upload.css +++ b/public_html/media/css/upload.css @@ -26,6 +26,11 @@ border-color: firebrick; } +#url-handler textarea { + width: 50%; + height: 6em; +} + .post .thumbnail { width: 100px; height: 100px; @@ -145,3 +150,13 @@ ul.tagit { .post .form-wrapper { overflow: hidden; } + +.tab.url { + display: none; +} +.tab { + margin-bottom: 1em; +} +.tab textarea { + margin-bottom: 0.5em; +} diff --git a/public_html/media/css/user-view.css b/public_html/media/css/user-view.css index b95748c9..a70ea43a 100644 --- a/public_html/media/css/user-view.css +++ b/public_html/media/css/user-view.css @@ -3,33 +3,6 @@ font-size: 90%; } -.tabs ul { - list-style-type: none; - margin: 0 0 1em 0; - padding: 0; - border-bottom: 1px solid #ccc; -} -.tabs li { - display: inline-block; -} - -.tabs li a { - display: inline-block; - padding: 0.5em 1em; - margin-bottom: -1px; -} - -.tabs li a { - border: 1px solid white; - border-bottom: 1px solid #ccc; - color: silver; -} -.tabs li.selected a { - border: 1px solid #ccc; - border-bottom: 1px solid white; - color: inherit; -} - .avatar-wrapper { text-align: center; } diff --git a/public_html/media/js/upload.js b/public_html/media/js/upload.js index deff1a9f..24e26c32 100644 --- a/public_html/media/js/upload.js +++ b/public_html/media/js/upload.js @@ -1,35 +1,36 @@ $(function() { + $('.tabs nav a').click(function(e) + { + e.preventDefault(); + var className = $(this).parents('li').attr('class').replace('selected', '').replace(/^\s+|\s+$/, ''); + $('.tabs nav li').removeClass('selected'); + $(this).parents('li').addClass('selected'); + $('.tab').hide(); + $('.tab.' + className).show(); + }); + var tags = []; $.getJSON('/tags?json', function(data) { tags = data['tags']; }); - var handler = $('#file-handler'); - handler.on('dragenter', function(e) + $('#file-handler').on('dragenter', function(e) { $(this).addClass('active'); - }); - - handler.on('dragleave', function(e) + }).on('dragleave', function(e) { $(this).removeClass('active'); - }); - - handler.on('dragover', function(e) + }).on('dragover', function(e) { e.preventDefault(); - }); - - handler.on('drop', function(e) + }).on('drop', function(e) { e.preventDefault(); handleFiles(e.originalEvent.dataTransfer.files); $(this).trigger('dragleave'); - }); - - handler.on('click', function(e) + }).on('click', function(e) { $(':file').show().focus().trigger('click').hide(); }); @@ -39,6 +40,16 @@ $(function() handleFiles(this.files); }); + + + $('#url-handler-wrapper button').click(function(e) + { + var urls = $('#url-handler-wrapper textarea').val().split(/\s+/); + handleURLs(urls); + }); + + + $('.post .move-down-trigger, .post .move-up-trigger').on('click', function() { var dir = $(this).hasClass('move-down-trigger') ? 'd' : 'u'; @@ -62,6 +73,7 @@ $(function() }); + function sendNextPost() { var posts = $('#upload-step2 .post'); @@ -73,12 +85,14 @@ $(function() var postDom = posts.first(); var url = postDom.find('form').attr('action') + '?json'; - var file = postDom.data('file'); + var sourceFile = postDom.data('file'); + var sourceUrl = postDom.data('url'); var tags = postDom.find('[name=tags]').val(); var safety = postDom.find('[name=safety]:checked').val(); var source = postDom.find('[name=source]').val(); var fd = new FormData(); - fd.append('file', file); + fd.append('file', sourceFile); + fd.append('url', sourceUrl); fd.append('tags', tags); fd.append('safety', safety); fd.append('source', source); @@ -145,17 +159,54 @@ $(function() }); function handleFiles(files) + { + handleInputs(files, function(postDom, file) + { + postDom.data('file', file); + $('.file-name strong', postDom).text(file.name); + + if (file.type.match('image.*')) + { + var img = postDom.find('img') + var reader = new FileReader(); + reader.onload = (function(theFile, img) + { + return function(e) + { + img.css('background-image', 'none'); + img.attr('src', e.target.result); + }; + })(file, img); + reader.readAsDataURL(file); + } + }); + } + + function handleURLs(urls) + { + handleInputs(urls, function(postDom, url) + { + postDom.data('url', url); + $('.file-name strong', postDom).text(url); + $('[name=source]', postDom).val(url); + + var img = postDom.find('img'); + img.css('background-image', 'none'); + img.attr('src', url); + }); + } + + function handleInputs(inputs, callback) { $('#upload-step1').fadeOut(function() { - for (var i = 0; i < files.length; i ++) + for (var i = 0; i < inputs.length; i ++) { - var file = files[i]; + var input = inputs[i]; var postDom = $('#post-template').clone(true); postDom.find('form').submit(false); postDom.removeAttr('id'); - postDom.data('file', file); - $('.file-name strong', postDom).text(file.name); + $('.posts').append(postDom); postDom.show(); @@ -164,26 +215,7 @@ $(function() tagItOptions.placeholderText = $('.tags input').attr('placeholder'); $('.tags input', postDom).tagit(tagItOptions); - if (!file.type.match('image.*')) - { - continue; - } - - var img = postDom.find('img') - var reader = new FileReader(); - reader.onload = (function(theFile, img) - { - return function(e) - { - /*img.css('max-width', img.css('width')); - img.css('max-height', img.css('height')); - img.css('width', 'auto'); - img.css('height', 'auto');*/ - img.css('background-image', 'none'); - img.attr('src', e.target.result); - }; - })(file, img); - reader.readAsDataURL(file); + callback(postDom, input); } $('#upload-step2').fadeIn(function() { diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 2a310100..b5f1ca3e 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -197,6 +197,7 @@ class PostController public function uploadAction() { $this->context->stylesheets []= 'upload.css'; + $this->context->stylesheets []= 'tabs.css'; $this->context->scripts []= 'upload.js'; $this->context->subTitle = 'upload'; PrivilegesHelper::confirmWithException(Privilege::UploadPost); @@ -206,12 +207,56 @@ class PostController if (InputHelper::get('submit')) { /* file contents */ - $suppliedFile = $_FILES['file']; - self::handleUploadErrors($suppliedFile); + if (isset($_FILES['file'])) + { + $suppliedFile = $_FILES['file']; + self::handleUploadErrors($suppliedFile); + $origName = basename($suppliedFile['name']); + $sourcePath = $suppliedFile['tmp_name']; + } + elseif (InputHelper::get('url')) + { + $url = InputHelper::get('url'); + if (!preg_match('/^https?:\/\//', $url)) + throw new SimpleException('Invalid URL "' . $url . '"'); + $origName = $url; + $sourcePath = tempnam(sys_get_temp_dir(), 'upload') . '.dat'; + + //warning: low level sh*t ahead + //download the URL $url into $sourcePath + $maxBytes = TextHelper::stripBytesUnits(ini_get('upload_max_filesize')); + set_time_limit(0); + $urlFP = fopen($url, 'rb'); + if (!$urlFP) + throw new SimpleException('Cannot open URL for reading'); + $sourceFP = fopen($sourcePath, 'w+b'); + if (!$sourceFP) + { + fclose($urlFP); + throw new SimpleException('Cannot open file for writing'); + } + try + { + while (!feof($urlFP)) + { + $buffer = fread($urlFP, 4 * 1024); + if (fwrite($sourceFP, $buffer) === false) + throw new SimpleException('Cannot write into file'); + fflush($sourceFP); + if (ftell($sourceFP) > $maxBytes) + throw new SimpleException('File is too big (maximum allowed size: ' . TextHelper::useBytesUnits($maxBytes) . ')'); + } + } + finally + { + fclose($urlFP); + fclose($sourceFP); + } + } /* file details */ - $mimeType = mime_content_type($suppliedFile['tmp_name']); + $mimeType = mime_content_type($sourcePath); $imageWidth = null; $imageHeight = null; switch ($mimeType) @@ -220,17 +265,17 @@ class PostController case 'image/png': case 'image/jpeg': $postType = PostType::Image; - list ($imageWidth, $imageHeight) = getimagesize($suppliedFile['tmp_name']); + list ($imageWidth, $imageHeight) = getimagesize($sourcePath); break; case 'application/x-shockwave-flash': $postType = PostType::Flash; - list ($imageWidth, $imageHeight) = getimagesize($suppliedFile['tmp_name']); + list ($imageWidth, $imageHeight) = getimagesize($sourcePath); break; default: throw new SimpleException('Invalid file type "' . $mimeType . '"'); } - $fileHash = md5_file($suppliedFile['tmp_name']); + $fileHash = md5_file($sourcePath); $duplicatedPost = R::findOne('post', 'file_hash = ?', [$fileHash]); if ($duplicatedPost !== null) throw new SimpleException('Duplicate upload: @' . $duplicatedPost->id); @@ -260,9 +305,9 @@ class PostController $dbPost = R::dispense('post'); $dbPost->type = $postType; $dbPost->name = $name; - $dbPost->orig_name = basename($suppliedFile['name']); + $dbPost->orig_name = $origName; $dbPost->file_hash = $fileHash; - $dbPost->file_size = filesize($suppliedFile['tmp_name']); + $dbPost->file_size = filesize($sourcePath); $dbPost->mime_type = $mimeType; $dbPost->safety = $suppliedSafety; $dbPost->source = $suppliedSource; @@ -274,7 +319,10 @@ class PostController $dbPost->ownFavoritee = []; $dbPost->sharedTag = $dbTags; - move_uploaded_file($suppliedFile['tmp_name'], $path); + if (is_uploaded_file($sourcePath)) + move_uploaded_file($sourcePath, $path); + else + rename($sourcePath, $path); R::store($dbPost); $this->context->transport->success = true; diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index d19b9c13..4f48808c 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -359,6 +359,7 @@ class UserController $page = 1; PrivilegesHelper::confirmWithException(Privilege::ViewUser, PrivilegesHelper::getIdentitySubPrivilege($user)); + $this->context->stylesheets []= 'tabs.css'; $this->context->stylesheets []= 'user-view.css'; $this->context->stylesheets []= 'post-list.css'; $this->context->stylesheets []= 'post-small.css'; diff --git a/src/Helpers/TextHelper.php b/src/Helpers/TextHelper.php index ee0ff418..40356bf2 100644 --- a/src/Helpers/TextHelper.php +++ b/src/Helpers/TextHelper.php @@ -63,6 +63,18 @@ class TextHelper return constant($constantName); } + private static function stripUnits($string, $base, $suffixes) + { + $suffix = substr($string, -1, 1); + $index = array_search($suffix, $suffixes); + if ($index === false) + return $string; + $number = intval($string); + for ($i = 0; $i < $index; $i ++) + $number *= $base; + return $number; + } + private static function useUnits($number, $base, $suffixes) { $suffix = array_shift($suffixes); @@ -89,6 +101,16 @@ class TextHelper return self::useUnits($number, 1000, ['', 'K', 'M']); } + public static function stripBytesUnits($string) + { + return self::stripUnits($string, 1024, ['B', 'K', 'M', 'G']); + } + + public static function stripDecimalUnits($string) + { + return self::stripUnits($string, 1000, ['', 'K', 'M']); + } + public static function removeUnsafeKeys(&$input, $regex) { if (is_array($input)) diff --git a/src/Views/post-upload.phtml b/src/Views/post-upload.phtml index 409a835e..002a3213 100644 --- a/src/Views/post-upload.phtml +++ b/src/Views/post-upload.phtml @@ -14,11 +14,40 @@
- -
-
- Drop files here!
- Or just click on this box. + + +
+ +
+
+ Drop files here!
+ Or just click on this box.

+
+
+
+ +
+
+
+ +
+
diff --git a/src/Views/post-view.phtml b/src/Views/post-view.phtml index ff0cdfd8..c39e04cb 100644 --- a/src/Views/post-view.phtml +++ b/src/Views/post-view.phtml @@ -96,7 +96,9 @@ - context->transport->post->orig_name, strrpos($this->context->transport->post->orig_name, '.') + 1)) ?> + 'JPG', 'image/gif' => 'GIF', 'image/png' => 'PNG', 'application/x-shockwave-flash' => 'SWF'] ?> + context->transport->post->mimeType ?> + context->transport->post->file_size) ?>