diff --git a/js/common.js b/js/common.js index d1df430b7b..5699b40231 100644 --- a/js/common.js +++ b/js/common.js @@ -300,3 +300,334 @@ var PMA_querywindow = (function ($, window) { } }; })(jQuery, window); + +/** + * Class to handle PMA Drag and Drop Import + * feature + */ +PMA_DROP_IMPORT = { + /** + * @var int, count of total uploads in this view + */ + uploadCount: 0, + /** + * @var int, count of live uploads + */ + liveUploadCount: 0, + /** + * @var string array, allowed extensions + */ + allowedExtensions: ['sql', 'xml', 'ldi', 'mediawiki', 'shp'], + /** + * @var string array, allowed extensions for compressed files + */ + allowedCompressedExtensions: ['gzip', 'bzip2', 'zip'], + /** + * @var obj array to store message returned by import_status.php + */ + importStatus: [], + /** + * Checks if any dropped file has valid extension or not + * + * @param string, filename + * + * @return string, extension for valid extension, '' otherwise + */ + _getExtension: function(file) { + var arr = file.split('.'); + ext = arr[arr.length - 1]; + + //check if compressed + if (jQuery.inArray(ext.toLowerCase(), + PMA_DROP_IMPORT.allowedCompressedExtensions) !== -1) { + ext = arr[arr.length - 2]; + } + + //Now check for extension + if (jQuery.inArray(ext.toLowerCase(), + PMA_DROP_IMPORT.allowedExtensions) !== -1) { + return ext; + } + return ''; + }, + /** + * Shows upload progress for different sql uploads + * + * @param: hash (string), hash for specific file upload + * @param: percent (float), file upload percentage + * + * @return void + */ + _setProgress: function(hash, percent) { + $('.pma_sql_import_status div li[data-hash="' +hash +'"]') + .children('progress').val(percent); + }, + /** + * Function to upload the file asyncronously + * + * @param formData, FormData object for a specific file + * @param hash, hash of the current file upload + * + * @return void + */ + _sendFileToServer: function(formData, hash) { + var uploadURL ="./import.php"; //Upload URL + var extraData ={}; + var jqXHR = $.ajax({ + xhr: function() { + var xhrobj = $.ajaxSettings.xhr(); + if (xhrobj.upload) { + xhrobj.upload.addEventListener('progress', function(event) { + var percent = 0; + var position = event.loaded || event.position; + var total = event.total; + if (event.lengthComputable) { + percent = Math.ceil(position / total * 100); + } + //Set progress + PMA_DROP_IMPORT._setProgress(hash, percent); + }, false); + } + return xhrobj; + }, + url: uploadURL, + type: "POST", + contentType:false, + processData: false, + cache: false, + data: formData, + success: function(data){ + PMA_DROP_IMPORT._importFinished(hash, false, data.success); + if (!data.success) { + PMA_DROP_IMPORT.importStatus[PMA_DROP_IMPORT.importStatus.length] = { + hash: hash, + message: data.error + }; + } + } + }); + + // -- provide link to cancel the upload + $('.pma_sql_import_status div li[data-hash="' +hash + +'"] span.filesize').html('' + +PMA_messages['dropImportMessageCancel'] +''); + + // -- add event listener to this link to abort upload operation + $('.pma_sql_import_status div li[data-hash="' +hash + +'"] span.filesize span.pma_drop_file_status') + .on('click', function() { + if ($(this).attr('task') === 'cancel') { + jqXHR.abort(); + $(this).html('' +PMA_messages['dropImportMessageAborted'] +''); + PMA_DROP_IMPORT._importFinished(hash, true, false); + } else if ($(this).children("span").html() === + PMA_messages['dropImportMessageFailed']) { + // -- view information + var $this = $(this); + $.each( PMA_DROP_IMPORT.importStatus, + function( key, value ) { + if (value.hash === hash) { + $(".pma_drop_result:visible").remove(); + var filename = $this.parent('span').attr('data-filename'); + $("body").append('

' + +PMA_messages['dropImportImportResultHeader'] +' - ' + +filename +'x

' +value.message +'
'); + $(".pma_drop_result").draggable(); //to make this dialog draggable + return; + } + }); + } + }); + }, + /** + * Triggered when an object is dragged into the PMA UI + * + * @param event obj + * + * @return void + */ + _dragenter : function (event) { + if (PMA_commonParams.get('db') === '') { + $(".pma_drop_handler").html(PMA_messages['dropImportSelectDB']); + } else { + $(".pma_drop_handler").html(PMA_messages['dropImportDropFiles']); + } + $(".pma_drop_handler").fadeIn(); + event.stopPropagation(); + event.preventDefault(); + }, + /** + * Triggered when dragged file is being dragged over PMA UI + * + * @param event obj + * + * @return void + */ + _dragover: function (event) { + $(".pma_drop_handler").fadeIn(); + event.stopPropagation(); + event.preventDefault(); + }, + /** + * Triggered when dragged objects are left + * + * @param event obj + * + * @return void + */ + _dragleave: function (event) { + event.stopPropagation(); + event.preventDefault(); + $(".pma_drop_handler").clearQueue().stop(); + $(".pma_drop_handler").fadeOut(); + $(".pma_drop_handler").html(PMA_messages['dropImportDropFiles']); + }, + /** + * Called when upload has finished + * + * @param string, uniques hash for a certain upload + * @param bool, true if upload was aborted + * @param bool, status of sql upload, as sent by server + * + * @return void + */ + _importFinished: function(hash, aborted, status) { + $('.pma_sql_import_status div li[data-hash="' +hash +'"]') + .children("progress").hide(); + var icon = 'icon ic_s_success'; + // -- provide link to view upload status + if (!aborted) { + if (status) { + $('.pma_sql_import_status div li[data-hash="' +hash + +'"] span.filesize span.pma_drop_file_status') + .html('' +PMA_messages['dropImportMessageSuccess'] +''); + } else { + $('.pma_sql_import_status div li[data-hash="' +hash + +'"] span.filesize span.pma_drop_file_status') + .html('' +PMA_messages['dropImportMessageFailed'] + +''); + icon = 'icon ic_s_error'; + } + } else { + icon = 'icon ic_s_notice'; + } + $('.pma_sql_import_status div li[data-hash="' +hash + +'"] span.filesize span.pma_drop_file_status') + .attr('task', 'info'); + + // Set icon + $('.pma_sql_import_status div li[data-hash="' +hash +'"]') + .prepend(' '); + + // Decrease liveUploadCount by one + $('.pma_import_count').html(--PMA_DROP_IMPORT.liveUploadCount); + if (!PMA_DROP_IMPORT.liveUploadCount) { + $('.pma_sql_import_status h2 .close').fadeIn(); + } + }, + /** + * Triggered when dragged objects are dropped to UI + * From this function, the AJAX Upload operation is initated + * + * @param event object + * + * @return void + */ + _drop: function (event) { + var dbname = PMA_commonParams.get('db'); + //if no database is selected -- no + if (dbname !== '') { + $(".pma_sql_import_status").slideDown(); + var files = event.originalEvent.dataTransfer.files; + for (var i = 0; i < files.length; i++) { + var ext = (PMA_DROP_IMPORT._getExtension(files[i].name)); + var hash = AJAX.hash(++PMA_DROP_IMPORT.uploadCount); + + $(".pma_sql_import_status div").append('
  • ' + +((ext !== '') ? '' : ' ') + +escapeHtml(files[i].name) + '' +(files[i].size/1024).toFixed(2) + +' kb
  • '); + + //scroll the UI to bottom + $(".pma_sql_import_status div").scrollTop( + $(".pma_sql_import_status div").scrollTop() + 50); //50 hardcoded for now + + if (ext !== '') { + // Increment liveUploadCount by one + $('.pma_import_count').html(++PMA_DROP_IMPORT.liveUploadCount); + $('.pma_sql_import_status h2 .close').fadeOut(); + + $('.pma_sql_import_status div li[data-hash="' +hash +'"]') + .append('
    '); + + //uploading + var fd = new FormData(); + fd.append('import_file', files[i]); + // todo: method to find the value below + fd.append('noplugin', '539de66e760ee'); + fd.append('db', dbname); + fd.append('token', PMA_commonParams.get('token')); + fd.append('import_type', 'database'); + // todo: method to find the value below + fd.append('MAX_FILE_SIZE', '4194304'); + // todo: method to find the value below + fd.append('charset_of_file','utf-8'); + // todo: method to find the value below + fd.append('allow_interrupt', 'yes'); + fd.append('skip_queries', '0'); + fd.append('format',ext); + fd.append('sql_compatibility','NONE'); + fd.append('sql_no_auto_value_on_zero','something'); + fd.append('ajax_request','true'); + fd.append('hash', hash); + + // init uploading + PMA_DROP_IMPORT._sendFileToServer(fd, hash); + } + } + } + $(".pma_drop_handler").fadeOut(); + event.stopPropagation(); + event.preventDefault(); + } +}; + +/** + * Called when some user drags, dragover, leave + * a file to the PMA UI + * @param object Event data + * @return void + */ +$(document).on('dragenter', PMA_DROP_IMPORT._dragenter); +$(document).on('dragover', PMA_DROP_IMPORT._dragover); +$(document).on('dragleave', '.pma_drop_handler', PMA_DROP_IMPORT._dragleave); + +//when file is dropped to PMA UI +$(document).on('drop', 'body', PMA_DROP_IMPORT._drop); + +// minimizing-maximising the sql ajax upload status +$(document).on('click', '.pma_sql_import_status h2 .minimize', function() { + if ($(this).attr('toggle') === 'off') { + $('.pma_sql_import_status div').css('height','270px'); + $(this).attr('toggle','on'); + } else { + $('.pma_sql_import_status div').css("height","0px"); + $(this).attr('toggle','off'); + } +}); + +// closing sql ajax upload status +$(document).on('click', '.pma_sql_import_status h2 .close', function() { + $('.pma_sql_import_status').fadeOut(function() { + $('.pma_sql_import_status div').html(''); + PMA_DROP_IMPORT.importStatus = []; //clear the message array + }); +}); + +// Closing the import result box +$(document).on('click', '.pma_drop_result h2 .close', function(){ + $(this).parent('h2').parent('div').remove(); +}); diff --git a/js/messages.php b/js/messages.php index 3e933545e0..b26ae9985a 100644 --- a/js/messages.php +++ b/js/messages.php @@ -400,6 +400,15 @@ $js_messages['strShowAllCol'] = __('Show all'); $js_messages['strAlertNonUnique'] = __('This table does not contain a unique column. Features related to the grid edit, checkbox, Edit, Copy and Delete links may not work after saving.'); $js_messages['strEnterValidHex'] = __('Please enter a valid hexadecimal string. Valid characters are 0-9, A-F.'); +/** Drag & Drop sql import messages */ +$js_messages['dropImportMessageCancel'] = __('cancel'); +$js_messages['dropImportMessageAborted'] = __('Aborted'); +$js_messages['dropImportMessageFailed'] = __('Failed'); +$js_messages['dropImportMessageSuccess'] = __('Success'); +$js_messages['dropImportImportResultHeader'] = __('Import status'); +$js_messages['dropImportDropFiles'] = __('Drop Files Here'); +$js_messages['dropImportSelectDB'] = __('Select Database First'); + // this approach does not work when the parameter is changed via user prefs switch ($GLOBALS['cfg']['GridEditing']) { case 'double-click': diff --git a/libraries/navigation/Navigation.class.php b/libraries/navigation/Navigation.class.php index 23da98590c..e075765f72 100644 --- a/libraries/navigation/Navigation.class.php +++ b/libraries/navigation/Navigation.class.php @@ -56,6 +56,7 @@ class PMA_Navigation // closes the tags that were opened by the navigation header $retval .= ''; $retval .= ''; + $retval .= $this->_getDropHandler(); $retval .= ''; } @@ -89,6 +90,26 @@ class PMA_Navigation PMA_queryAsControlUser($sqlQuery, false); } + /** + * Inserts Drag and Drop Import handler + * + * @param: void + * @return: html code for drop handler + */ + private function _getDropHandler() + { + $retval = ''; + $retval .= '
    ' .__('Drop files here') .'
    '; + $retval .= '
    '; + $retval .= '

    SQL upload ( '; + $retval .= '0 '; + $retval .= ') x'; + $retval .= '-

    '; + $retval .= '
    '; + $retval .= '
    '; + return $retval; + } + /** * Remove a hidden item of navigation tree from the * list of hidden items in PMA database. diff --git a/themes/original/css/common.css.php b/themes/original/css/common.css.php index 6bbafeafcc..c5b20d9476 100644 --- a/themes/original/css/common.css.php +++ b/themes/original/css/common.css.php @@ -2954,3 +2954,115 @@ html.ie7 #pma_console .query_input { padding-bottom: 2em; cursor: text; } + +.pma_drop_handler { + display: none; + position: fixed; + top: 0px; + left: 0px; + width: 100%; + background: rgba(0, 0, 0, 0.6); + height: 100%; + z-index: 999; + color: white; + font-size: 30pt; + text-align: center; + padding-top: 20%; +} + +.pma_sql_import_status { + display: none; + position: fixed; + bottom: 0px; + right: 25px; + width: 400px; + border: 1px solid #999; + background: #f3f3f3; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: 2px 2px 5px #ccc; + -webkit-box-shadow: 2px 2px 5px #ccc; + box-shadow: 2px 2px 5px #ccc; +} + +.pma_sql_import_status h2, +.pma_drop_result h2 { + background-color: #bbb; + padding: .1em .3em; + margin-top: 0; + margin-bottom: 0; + color: #fff; + font-size: 1.6em; + font-weight: normal; + text-shadow: 0 1px 0 #777; + -moz-box-shadow: 1px 1px 15px #999 inset; + -webkit-box-shadow: 1px 1px 15px #999 inset; + box-shadow: 1px 1px 15px #999 inset; +} + +.pma_sql_import_status div { + height: 270px; + overflow-y:auto; + overflow-x:hidden; + list-style-type: none; +} + +.pma_sql_import_status div li { + padding: 8px 10px; + border-bottom: 1px solid #bbb; + color: rgb(148, 14, 14); + background: white; +} + +.pma_sql_import_status div li .filesize { + float: right; +} + +.pma_sql_import_status h2 .minimize { + float: right; + margin-right: 5px; + padding: 0px 10px; +} + +.pma_sql_import_status h2 .close { + float: right; + margin-right: 5px; + padding: 0px 10px; + display: none; +} + +.pma_sql_import_status h2 .minimize:hover, +.pma_sql_import_status h2 .close:hover, +.pma_drop_result h2 .close:hover { + background: rgba(155, 149, 149, 0.78); + cursor: pointer; +} + +.pma_drop_file_status { + color: #235a81; +} + +.pma_drop_file_status span.underline:hover { + cursor: pointer; + text-decoration: underline; +} + +.pma_drop_result { + position: fixed; + top: 10%; + left: 20%; + width: 60%; + background: white; + min-height: 300px; + z-index: 800; + -webkit-box-shadow: 0px 0px 15px #999; + border-radius: 10px; + cursor: move; +} + +.pma_drop_result h2 .close { + float: right; + margin-right: 5px; + padding: 0px 10px; +} diff --git a/themes/pmahomme/css/common.css.php b/themes/pmahomme/css/common.css.php index 0b1c6bc940..d434c3d19c 100644 --- a/themes/pmahomme/css/common.css.php +++ b/themes/pmahomme/css/common.css.php @@ -3251,3 +3251,115 @@ html.ie7 #pma_console .query_input { padding-bottom: 2em; cursor: text; } + +.pma_drop_handler { + display: none; + position: fixed; + top: 0px; + left: 0px; + width: 100%; + background: rgba(0, 0, 0, 0.6); + height: 100%; + z-index: 999; + color: white; + font-size: 30pt; + text-align: center; + padding-top: 20%; +} + +.pma_sql_import_status { + display: none; + position: fixed; + bottom: 0px; + right: 25px; + width: 400px; + border: 1px solid #999; + background: #f3f3f3; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: 2px 2px 5px #ccc; + -webkit-box-shadow: 2px 2px 5px #ccc; + box-shadow: 2px 2px 5px #ccc; +} + +.pma_sql_import_status h2, +.pma_drop_result h2 { + background-color: #bbb; + padding: .1em .3em; + margin-top: 0; + margin-bottom: 0; + color: #fff; + font-size: 1.6em; + font-weight: normal; + text-shadow: 0 1px 0 #777; + -moz-box-shadow: 1px 1px 15px #999 inset; + -webkit-box-shadow: 1px 1px 15px #999 inset; + box-shadow: 1px 1px 15px #999 inset; +} + +.pma_sql_import_status div { + height: 270px; + overflow-y:auto; + overflow-x:hidden; + list-style-type: none; +} + +.pma_sql_import_status div li { + padding: 8px 10px; + border-bottom: 1px solid #bbb; + color: rgb(148, 14, 14); + background: white; +} + +.pma_sql_import_status div li .filesize { + float: right; +} + +.pma_sql_import_status h2 .minimize { + float: right; + margin-right: 5px; + padding: 0px 10px; +} + +.pma_sql_import_status h2 .close { + float: right; + margin-right: 5px; + padding: 0px 10px; + display: none; +} + +.pma_sql_import_status h2 .minimize:hover, +.pma_sql_import_status h2 .close:hover, +.pma_drop_result h2 .close:hover { + background: rgba(155, 149, 149, 0.78); + cursor: pointer; +} + +.pma_drop_file_status { + color: #235a81; +} + +.pma_drop_file_status span.underline:hover { + cursor: pointer; + text-decoration: underline; +} + +.pma_drop_result { + position: fixed; + top: 10%; + left: 20%; + width: 60%; + background: white; + min-height: 300px; + z-index: 800; + -webkit-box-shadow: 0px 0px 15px #999; + border-radius: 10px; + cursor: move; +} + +.pma_drop_result h2 .close { + float: right; + margin-right: 5px; + padding: 0px 10px; +}