Răsfoiți Sursa

Added preview for
- Video (HTML5)
- Audio (HTML5)
- PDF (Browser capabilities)
attachment.
Added drag & drop functionality
Added attachment preview to preview before submitting

thororm 9 ani în urmă
părinte
comite
faf596aeb7
8 a modificat fișierele cu 238 adăugiri și 58 ștergeri
  1. 1 1
      cfg/conf.ini.sample
  2. 14 1
      css/bootstrap/privatebin.css
  3. 20 8
      css/privatebin.css
  4. 2 0
      i18n/de.json
  5. 193 42
      js/privatebin.js
  6. 1 1
      lib/Configuration.php
  7. 4 3
      tpl/bootstrap.php
  8. 3 2
      tpl/page.php

+ 1 - 1
cfg/conf.ini.sample

@@ -64,7 +64,7 @@ languageselection = false
 ; scripts or run your site behind certain DDoS-protection services.
 ; Check the documentation at https://content-security-policy.com/
 ; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions.
-; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups"
+; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; media-src 'self' data:; object-src  'self' data:; Referrer-Policy: 'no-referrer'"
 
 ; stay compatible with PrivateBin Alpha 0.19, less secure
 ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of

+ 14 - 1
css/bootstrap/privatebin.css

@@ -64,12 +64,25 @@ body.navbar-spacing {
 	margin-right: 8px;
 }
 
-#image img {
+#attachmentPreview img {
 	max-width: 100%;
 	height: auto;
 	margin-bottom: 20px;
 }
 
+#attachmentPreview .pdfPreview {
+	width: 100%;
+	height: 100vh;
+	margin-bottom: 20px;
+}
+
+.dragAndDropFile{
+        color:#777;
+        font-size:1em;
+        display:inline;
+}
+
+
 #deletelink {
 	float: right;
 }

+ 20 - 8
css/privatebin.css

@@ -72,13 +72,13 @@ h3.title {
 	bottom: 8px;
 }
 
-#aboutbox { 
-	color: #94a3b4; 
+#aboutbox {
+	color: #94a3b4;
 	padding: 4px 8px 4px 16px;
-	position: relative; 
+	position: relative;
 	top: 10px;
 	border-left: 2px solid #94a3b4;
-	float: right;  
+	float: right;
 	width: 60%;
 }
 
@@ -104,17 +104,29 @@ h3.title {
 	font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, monospace;
 }
 
-#image img {
+#attachmentPreview img {
 	max-width: 100%;
 	height: auto;
 }
 
-#status { 
+#attachmentPreview .pdfPreview {
+	width: 100%;
+	height: 100vh;
+	margin-bottom: 20px;
+}
+
+.dragAndDropFile{
+        color:#777;
+        font-size:1em;
+        display:inline;
+}
+
+#status {
 	clear: both;
 	padding: 5px 10px;
 }
 
-#pasteresult { 
+#pasteresult {
 	background-color: #1F2833;
 	color: #fff;
 	padding: 4px 12px;
@@ -249,7 +261,7 @@ input {
 	font-weight: bold !important;
 }
 
-#image, .nonworking {
+#attachmentPreview, .nonworking {
 	background-color: #fff;
 	color: #000;
 	width: 100%;

+ 2 - 0
i18n/de.json

@@ -131,6 +131,8 @@
     "Download attachment": "Anhang herunterladen",
     "Cloned file attached.": "Kopierte Datei angehängt.",
     "Attach a file": "Datei anhängen",
+    "or drag & drop file": "oder per Drag & Drop einfügen",
+    "File too large, to display a preview. Please download the attachment.": "Datei zu groß, um als Vorschau angezeigt zu werden. Bitte Anhang herunterladen.",
     "Remove attachment": "Anhang entfernen",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Dein Browser unterstützt das hochladen von verschlüsselten Dateien nicht. Bitte verwende einen neueren Browser.",

+ 193 - 42
js/privatebin.js

@@ -339,6 +339,180 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         }
     };
 
+    /**
+     * static attachment helper methods
+     *
+     * @name helper
+     * @class
+     */
+    var attachmentHelpers = {
+        attachmentData: undefined,
+        file: undefined,
+
+        /*
+         * Read file data as dataURL using the FileReader API
+         * https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()
+         */
+        readFileData: function (file) {
+            if (typeof FileReader === undefined) {
+                // revert loading status…
+                this.stateNewPaste();
+                this.showError(i18n._('Your browser does not support uploading encrypted files. Please use a newer browser.'));
+                return;
+            }
+
+            var fr = new FileReader();
+            if (file === undefined) {
+                file = controller.fileInput[0].files[0];
+                $('#dragAndDropFileName').text('');
+            } else {
+                $('#dragAndDropFileName').text(file.name);
+            }
+
+            attachmentHelpers.file = file;
+
+            fr.onload = function (e) {
+                var dataURL = e.target.result;
+                attachmentHelpers.attachmentData = dataURL;
+
+                if (controller.messagePreview.parent().hasClass('active')) {
+                    attachmentHelpers.handleFilePreviews(controller.attachmentPreview, dataURL);
+                }
+            };
+            fr.readAsDataURL(file);
+        },
+
+        /**
+         *  Handle the preview of files.
+         *  @argument {DOM Element} element where the preview should be appended.
+         *  @argument {File Data} data of the file to be displayed.
+         */
+        handleFilePreviews: function (element, data) {
+            if (data) {
+                var mimeType = this.getMimeTypeFromDataURL(data);
+
+                if (mimeType.match(/image\//i)) {
+                    this.showImagePreview(element, data);
+                } else if (mimeType.match(/video\//i)) {
+                    this.showVideoPreview(element, data, mimeType);
+                } else if (mimeType.match(/audio\//i)) {
+                    this.showAudioPreview(element, data, mimeType);
+                } else if (mimeType.match(/\/pdf/i)) {
+                    this.showPDFPreview(element, data);
+                }
+                //else {
+                //console.log("file but no image/video/audio/pdf");
+                //}
+            }
+        },
+
+        /**
+         * Get Mime Type from a DataURL
+         *
+         * @param {type} dataURL
+         * @returns Mime Type from a dataURL as obtained for a file using the FileReader API https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()
+         */
+        getMimeTypeFromDataURL: function (dataURL) {
+            return dataURL.slice(dataURL.indexOf('data:') + 5, dataURL.indexOf(';base64,'));
+        },
+
+        showImagePreview: function (element, image) {
+            element.html(
+                    $(document.createElement('img'))
+                    .attr('src', image)
+                    .attr('class', 'img-thumbnail')
+                    );
+            element.removeClass('hidden');
+        },
+
+        showVideoPreview: function (element, video, mimeType) {
+            var videoPlayer = $(document.createElement('video'))
+                    .attr('controls', 'true')
+                    .attr('autoplay', 'true')
+                    .attr('loop', 'true')
+                    .attr('class', 'img-thumbnail');
+
+            videoPlayer.append($(document.createElement('source'))
+                    .attr('type', mimeType)
+                    .attr('src', video));
+            element.html(videoPlayer);
+            element.removeClass('hidden');
+        },
+
+        showAudioPreview: function (element, audio, mimeType) {
+            var audioPlayer = $(document.createElement('audio'))
+                    .attr('controls', 'true')
+                    .attr('autoplay', 'true');
+
+            audioPlayer.append($(document.createElement('source'))
+                    .attr('type', mimeType)
+                    .attr('src', audio));
+            element.html(audioPlayer);
+            element.removeClass('hidden');
+        },
+
+        showPDFPreview: function (element, pdf) {
+            //PDFs are only displayed if the filesize is smaller than about 1MB (after base64 encoding).
+            //Bigger filesizes currently cause crashes in various browsers.
+            //See also: https://code.google.com/p/chromium/issues/detail?id=69227
+
+            //Firefox crashes with files that are about 1.5MB
+            //The performance with 1MB files is bareable
+            if (pdf.length < 1398488) {
+
+                //Fallback for browsers, that don't support the vh unit
+                var clientHeight = $(window).height();
+
+                element.html(
+                        $(document.createElement('embed'))
+                        .attr('src', pdf)
+                        .attr('type', 'application/pdf')
+                        .attr('class', 'pdfPreview')
+                        .css('height', clientHeight)
+                        );
+                element.removeClass('hidden');
+            } else {
+                controller.showError(i18n._('File too large, to display a preview. Please download the attachment.'));
+            }
+        },
+
+        addDragDropHandler: function () {
+            var fileInput = controller.fileInput;
+
+            if (fileInput.length === 0) {
+                return;
+            }
+
+            function ignoreDragDrop(e) {
+                e.stopPropagation();
+                e.preventDefault();
+            }
+
+            function drop(e) {
+                e.stopPropagation();
+                e.preventDefault();
+
+                if (fileInput) {
+                    var file = e.dataTransfer.files[0];
+                    //Clear the file input:
+                    fileInput.wrap('<form>').closest('form').get(0).reset();
+                    fileInput.unwrap();
+                    //Only works in Chrome:
+                    //fileInput[0].files = e.dataTransfer.files;
+
+                    attachmentHelpers.readFileData(file);
+                }
+            }
+
+            document.addEventListener("drop", drop, false);
+            document.addEventListener("dragenter", ignoreDragDrop, false);
+            document.addEventListener("dragover", ignoreDragDrop, false);
+            fileInput.on("change", function () {
+                attachmentHelpers.readFileData();
+            });
+        }
+    };
+
     /**
      * internationalization methods
      *
@@ -748,18 +922,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                         }
                         this.attachmentLink.attr('href', attachment);
                         this.attachment.removeClass('hidden');
+                        attachmentHelpers.handleFilePreviews(this.attachmentPreview, attachment);
 
-                        // if the attachment is an image, display it
-                        var imagePrefix = 'data:image/';
-                        if (attachment.substring(0, imagePrefix.length) === imagePrefix)
-                        {
-                            this.image.html(
-                                $(document.createElement('img'))
-                                    .attr('src', attachment)
-                                    .attr('class', 'img-thumbnail')
-                            );
-                            this.image.removeClass('hidden');
-                        }
                     }
                     var cleartext = filter.decipher(key, password, paste.data);
                     if (cleartext.length === 0 && password.length === 0 && !paste.attachment)
@@ -1018,11 +1182,12 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         sendData: function(event)
         {
             event.preventDefault();
-            var file = document.getElementById('file'),
-                files = (file && file.files) ? file.files : null; // FileList object
+
+            var fileName = attachmentHelpers.file ? attachmentHelpers.file.name : this.attachmentLink.attr('download');
+            var attachmentData = attachmentHelpers.attachmentData || this.attachmentLink.attr('href');
 
             // do not send if no data.
-            if (this.message.val().length === 0 && !(files && files[0]))
+            if (this.message.val().length === 0 && !(fileName && attachmentData))
             {
                 return;
             }
@@ -1045,35 +1210,12 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
 
             var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0),
                 password = this.passwordInput.val();
-            if(files && files[0])
+            if(fileName)
             {
-                if(typeof FileReader === undefined)
-                {
-                    // revert loading status…
-                    this.stateNewPaste();
-                    this.showError(i18n._('Your browser does not support uploading encrypted files. Please use a newer browser.'));
-                    return;
-                }
-                var reader = new FileReader();
-                // closure to capture the file information
-                reader.onload = (function(theFile)
-                {
-                    return function(e) {
-                        controller.sendDataContinue(
-                            randomkey,
-                            filter.cipher(randomkey, password, e.target.result),
-                            filter.cipher(randomkey, password, theFile.name)
-                        );
-                    };
-                })(files[0]);
-                reader.readAsDataURL(files[0]);
-            }
-            else if(this.attachmentLink.attr('href'))
-            {
-                this.sendDataContinue(
+                controller.sendDataContinue(
                     randomkey,
-                    filter.cipher(randomkey, password, this.attachmentLink.attr('href')),
-                    this.attachmentLink.attr('download')
+                    filter.cipher(randomkey, password, attachmentData),
+                    filter.cipher(randomkey, password, fileName)
                 );
             }
             else
@@ -1199,6 +1341,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         {
             this.message.text('');
             this.attachment.addClass('hidden');
+            this.attachmentPreview.addClass('hidden');
             this.cloneButton.addClass('hidden');
             this.rawTextButton.addClass('hidden');
             this.remainingTime.addClass('hidden');
@@ -1317,6 +1460,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                 this.preview.addClass('hidden');
             }
 
+            this.attachmentPreview.removeClass('hidden');
             this.pasteResult.addClass('hidden');
             this.message.addClass('hidden');
             this.clearText.addClass('hidden');
@@ -1547,6 +1691,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             this.message.focus();
             this.stateExistingPaste(true);
             this.formatPaste($('#pasteFormatter').val(), this.message.val());
+            attachmentHelpers.handleFilePreviews(this.attachmentPreview, attachmentHelpers.attachmentData);
         },
 
         /**
@@ -1602,6 +1747,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             this.message.text('');
             this.changeBurnAfterReading();
             this.changeOpenDisc();
+            attachmentHelpers.addDragDropHandler();
         },
 
         /**
@@ -1618,6 +1764,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             // the only way to deselect the file is to recreate the input
             this.fileWrap.html(this.fileWrap.html());
             this.fileWrap.removeClass('hidden');
+
+            attachmentHelpers.file = undefined;
+            attachmentHelpers.attachmentData = undefined;
         },
 
         /**
@@ -1776,7 +1925,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             this.fileRemoveButton = $('#fileremovebutton');
             this.fileWrap = $('#filewrap');
             this.formatter = $('#formatter');
-            this.image = $('#image');
+            this.attachmentPreview = $('#attachmentPreview');
+            this.fileInput = $('#file');
             this.loadingIndicator = $('#loadingindicator');
             this.message = $('#message');
             this.messageEdit = $('#messageedit');
@@ -1846,6 +1996,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
 
     return {
         helper: helper,
+        attachmentHelpers: attachmentHelpers,
         i18n: i18n,
         filter: filter,
         controller: controller

+ 1 - 1
lib/Configuration.php

@@ -51,7 +51,7 @@ class Configuration
             'languagedefault'          => '',
             'urlshortener'             => '',
             'icon'                     => 'identicon',
-            'cspheader'                => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups',
+            'cspheader'                => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; media-src \'self\' data:; object-src \'self\' data:; Referrer-Policy: \'no-referrer\'',
             'zerobincompatibility'     => false,
         ),
         'expire' => array(

+ 4 - 3
tpl/bootstrap.php

@@ -69,7 +69,7 @@ if ($MARKDOWN):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vYYJYraxQNOf41XtehLBU2JbIQ2Uffe+n8TjHyWkpqoZdZX4aL5zyABrUNvRUP02+AxoRsmNJkpvIbmeQqcIXg==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/n0xL/OCmeki+nYzqkpLjJNaoEcEBR3cVxvjXF9ESuqVuD81WpicSm2ZF/bHA+pjNuC0QzMBzsKaQwBI96/6JA==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
@@ -292,8 +292,9 @@ if ($FILEUPLOAD):
 						<ul class="dropdown-menu">
 							<li id="filewrap">
 								<div>
-									<input type="file" id="file" name="file" />
+									<input type="file" id="file" name="file"/>
 								</div>
+								<div id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('or drag & drop file'); ?></div>
 							</li>
 							<li>
 								<a id="fileremovebutton"  href="#">
@@ -447,7 +448,7 @@ endif;
 		</header>
 		<section class="container">
 			<article class="row">
-				<div id="image" class="col-md-12 text-center hidden"></div>
+				<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>
 				<div id="prettymessage" class="col-md-12 hidden">
 					<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
 				</div>

+ 3 - 2
tpl/page.php

@@ -47,7 +47,7 @@ if ($MARKDOWN):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vYYJYraxQNOf41XtehLBU2JbIQ2Uffe+n8TjHyWkpqoZdZX4aL5zyABrUNvRUP02+AxoRsmNJkpvIbmeQqcIXg==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/n0xL/OCmeki+nYzqkpLjJNaoEcEBR3cVxvjXF9ESuqVuD81WpicSm2ZF/bHA+pjNuC0QzMBzsKaQwBI96/6JA==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
@@ -203,6 +203,7 @@ if ($FILEUPLOAD):
 				<div id="attach" class="hidden">
 					<span id="clonedfile" class="hidden"><?php echo I18n::_('Cloned file attached.'); ?></span>
 					<span id="filewrap"><?php echo I18n::_('Attach a file'); ?>: <input type="file" id="file" name="file" /></span>
+					<span id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('or drag & drop file'); ?></span>
 					<button id="fileremovebutton"><?php echo I18n::_('Remove attachment'); ?></button>
 				</div>
 <?php
@@ -212,7 +213,7 @@ endif;
 					<button id="messageedit"><?php echo I18n::_('Editor'); ?></button>
 					<button id="messagepreview"><?php echo I18n::_('Preview'); ?></button>
 				</div>
-				<div id="image" class="hidden"></div>
+				<div id="attachmentPreview" class="hidden"></div>
 				<div id="prettymessage" class="hidden">
 					<pre id="prettyprint" class="prettyprint linenums:1"></pre>
 				</div>