diff --git a/README.md b/README.md index 4f1c099..1d629aa 100755 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ Task List: * Clean up update script * Fix broken themes * Re Add the custom language recognition system after recode +* Remove all old and unneeded dependencies +* Update all current components to use more current standards ( async await and .then in favor over callbacks ) Completed: diff --git a/components/active/init.js b/components/active/init.js index 7d6b8f8..64661f8 100755 --- a/components/active/init.js +++ b/components/active/init.js @@ -522,20 +522,30 @@ original: session.untainted, changed: newContent }, function( success, patch ) { + + console.log( "diff test", success, patch ); + if( success ) { - codiad.filemanager.savePatch( path, patch, session.serverMTime, { - success: handleSuccess - }, alerts ); + if( patch.length > 0 ) { + + codiad.filemanager.save_file( path, { + patch: patch, + mtime: session.serverMTime + }, alerts ) + .then( handleSuccess ); + } } else { - codiad.filemanager.saveFile( path, newContent, { - success: handleSuccess - }, alerts ); + + console.log( "calling save while failed diff", path, newContent, alerts ); + codiad.filemanager.save_file( path, newContent, alerts ) + .then( handleSuccess ); } }, this ); } else { - codiad.filemanager.saveFile( path, newContent, { - success: handleSuccess - }, alert ); + + console.log( "calling save without mtime and untainted", path, newContent, alerts ); + codiad.filemanager.save_file( path, newContent, alerts ) + .then( handleSuccess ); } }, diff --git a/components/autosave/init.js b/components/autosave/init.js index b45c4be..1873b13 100755 --- a/components/autosave/init.js +++ b/components/autosave/init.js @@ -228,7 +228,11 @@ _this.content = content; codiad.active.save; - codiad.filemanager.saveFile( path, content, localStorage.removeItem( path ), false ); + codiad.filemanager.save_file( path, {content: content}, false ) + .then( function( i ) { + + localStorage.removeItem( path ); + }); let session = codiad.active.sessions[path]; if( typeof session != 'undefined' ) { diff --git a/components/filemanager/class.filemanager.php b/components/filemanager/class.filemanager.php index 7f57bfd..ec52b5c 100755 --- a/components/filemanager/class.filemanager.php +++ b/components/filemanager/class.filemanager.php @@ -436,7 +436,7 @@ class Filemanager extends Common { // MODIFY (Modifies a file name/contents or directory name) ////////////////////////////////////////////////////////////////// - public function modify( $path, $content, $patch=false, $mtime=0 ) { + public function modify( $path, $content, $patch = false, $mtime = 0 ) { // Change content $response = array( @@ -734,12 +734,75 @@ class Filemanager extends Common { return $response; } + public function stitch( $path ) { + + $response = array( + "status" => "none", + "message" => "", + ); + + if( ! Permissions::has_write( $path ) ) { + + $response["status"] = "error"; + $response["message"] = "You do not have access to write to this file."; + return $response; + } + + if( ! common::isAbsPath( $path ) ) { + + $path = WORKSPACE . "/$path"; + } + + $path = $_POST["path"]; + $tmp = DATA . "tmp/$path/"; + $dir = dirname( $path ); + $name = basename( $path ); + $files = scandir( $tmp ); + + if( ! is_dir( $dir ) ) { + + mkdir( $dir, 0755, true ); + } + + foreach( $files as $id => $file ) { + + if( $file !== "." && $file !== ".." ) { + + $data = file_get_contents( $cache_path . $file ); + $handle = fopen( $path, "a" ); + $status = fwrite( $handle, $data ); + fclose( $handle ); + unlink( $cache_path . $file ); + } + } + + $tmp_array = explode( "/", $path ); + $remove = array(); + + while( count( $tmp_array ) > 0 ) { + + $remove[] = DATA . "tmp/" . implode( "/", $tmp_array ); + array_pop( $tmp_array ); + } + + foreach( $tmp_array as $id => $i ) { + + rmdir( $i ); + } + return $response; + } + ////////////////////////////////////////////////////////////////// // UPLOAD (Handles uploads to the specified directory) ////////////////////////////////////////////////////////////////// public function upload( $path, $blob ) { + $response = array( + "status" => "none", + "message" => "", + ); + // Check that the path is a directory if( ! Permissions::has_write( $path ) ) { @@ -753,24 +816,27 @@ class Filemanager extends Common { $path = WORKSPACE . "/$path"; } + $dirname = dirname( $path ); + $name = basename( $path ); + + $blob = @file_get_contents( $_POST["data"] ); + $path = $_POST["path"]; + $index = $_POST["index"]; $response = array( "status" => "none", "message" => "", ); - $dirname = dirname( $path ); - $name = basename( $path ); + $tmp = DATA . "tmp/"; - if( ! is_dir( $dirname ) ) { + if( ! is_dir( $tmp . $path ) ) { - mkdir( $dirname, 0755, true ); + mkdir( $tmp . $path, 0755, true ); } - $handle = fopen( $path, "a" ); + $handle = fopen( "$tmp$path/$index", "a" ); $status = fwrite( $handle, $blob ); fclose( $handle ); - //$status = file_put_contents( $path, $blob, FILE_APPEND ); - if( $status === false ) { $response["status"] = "error"; @@ -782,7 +848,6 @@ class Filemanager extends Common { $response["bytes"] = $status; $response["message"] = "$status bytes written to file."; } - return $response; } } diff --git a/components/filemanager/controller.php b/components/filemanager/controller.php index c764d4a..8f54ac9 100755 --- a/components/filemanager/controller.php +++ b/components/filemanager/controller.php @@ -260,6 +260,11 @@ switch( $action ) { } break; + case( "stitch" ): + + $response = $Filemanager->stitch( $path ); + break; + case 'unarchive': if( ! isset( $path ) ) { diff --git a/components/filemanager/init.js b/components/filemanager/init.js index 2a186a6..cd2ae80 100755 --- a/components/filemanager/init.js +++ b/components/filemanager/init.js @@ -18,7 +18,7 @@ clipboard: '', controller: 'components/filemanager/controller.php', dialog: 'components/filemanager/dialog.php', - filelist: { + file_previewlist: { audio: [ 'aac', @@ -226,8 +226,8 @@ action: 'create', type: type, path: path - }, - function( container ) { + }) + .then( function( container ) { $( '#modal-content form' ) .on( 'submit', function( e ) { @@ -274,43 +274,45 @@ codiad.modal.load( 400, this.dialog, { action: 'delete', path: path - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { + }) + .then( function( container ) { - e.preventDefault(); - $.get( _this.controller + '?action=delete&path=' + encodeURIComponent( path ), function( data ) { + $( '#modal-content form' ) + .on( 'submit', function( e ) { - - let response = codiad.jsend.parse( data ); - if( response != 'error' ) { + e.preventDefault(); + $.get( _this.controller + '?action=delete&path=' + encodeURIComponent( path ), function( data ) { - let node = $( '#file-manager a[data-path="' + path + '"]' ); - let parent_path = node.parent().parent().children( 'a' ).attr( 'data-path' ); - node.parent( 'li' ).remove(); - - // Close any active files - $( '#active-files a' ).each( function() { + let response = codiad.jsend.parse( data ); + if( response != 'error' ) { - let curPath = $( this ).attr( 'data-path' ); + let node = $( '#file-manager a[data-path="' + path + '"]' ); + let parent_path = node.parent().parent().children( 'a' ).attr( 'data-path' ); + node.parent( 'li' ).remove(); - console.log( curPath, curPath.indexOf( path ) ); - - if( curPath.indexOf( path ) == 0 ) { + // Close any active files + $( '#active-files a' ).each( function() { - codiad.active.remove( curPath ); - } - }); - - /* Notify listeners. */ - amplify.publish( 'filemanager.onDelete', { - deletePath: path, - path: parent_path - }); - } - codiad.modal.unload(); + let curPath = $( this ).attr( 'data-path' ); + + console.log( curPath, curPath.indexOf( path ) ); + + if( curPath.indexOf( path ) == 0 ) { + + codiad.active.remove( curPath ); + } + }); + + /* Notify listeners. */ + amplify.publish( 'filemanager.onDelete', { + deletePath: path, + path: parent_path + }); + } + codiad.modal.unload(); + }); }); - }); + }) }, delete_children_nodes: function( path ) { @@ -541,6 +543,13 @@ return files; }, + get_parent: function( path ) { + + let parent = path.split( '/' ); + parent.pop(); + return parent.join( '/' ); + }, + get_short_name: function( path ) { return path.split( '/' ).pop(); @@ -881,7 +890,7 @@ _this.download( path ); } else { - _this.openInModal( path ); + _this.open_in_modal( path ); } } else { @@ -900,57 +909,77 @@ _this.dialog, { action: 'selector', - }, - async function( container ) { + } + ) + .then( async function( container ) { + + let _this = codiad.filemanager; + let div = $( "
" ); + let select = $( '' ); + let cancel = $( '' ); + + if( ! path ) { - let _this = codiad.filemanager; - let div = $( "
" ); - let select = $( '' ); - let cancel = $( '' ); + path = codiad.project.getCurrent(); + } + + if( Object.keys( filters ).length == 0 ) { - if( ! path ) { + filters = { - path = codiad.project.getCurrent(); + type: 'directory', } + } + + _this.selector_listeners( container, filters, callbacks ); + + let result = await _this.index( + path, + false, + "create", + filters, + directory_callbacks, + file_callbacks, + ); + + container.html( div ); + container.append( select ); + container.append( cancel ); + + select.on( 'click', function( e ) { - if( Object.keys( filters ).length == 0 ) { - - filters = { - - type: 'directory', - } - } + codiad.modal.unload(); + resolve( _this.selected ); + }); + cancel.on( 'click', function( e ) { - _this.selector_listeners( container, filters, callbacks ); - - let result = await _this.index( - path, - false, - "create", - filters, - directory_callbacks, - file_callbacks, - ); - - container.html( div ); - container.append( select ); - container.append( cancel ); - - select.on( 'click', function( e ) { - - codiad.modal.unload(); - resolve( _this.selected ); + codiad.modal.unload(); + reject({ + status: "error", + message: "User canceled action." }); - cancel.on( 'click', function( e ) { - - codiad.modal.unload(); - reject({ - status: "error", - message: "User canceled action." - }); - }); - }, - ); + }); + }) + .error( reject ); + }); + }, + + open_in_modal: function( path ) { + + let type = ""; + let ext = this.getExtension( path ).toLowerCase(); + + if( this.file_previewlist.images.includes( ext ) ) { + + type = 'music_preview'; + } else if( this.file_previewlist.images.includes( ext ) ) { + + type = 'preview'; + } + + codiad.modal.load( 250, this.dialog, { + action: type, + path: path }); }, @@ -965,8 +994,8 @@ path: path, short_name: shortName, type: type - }, - function( content ) { + }) + .then( function( content ) { $( content ).find( 'form' ) .on( 'submit', function( e ) { @@ -986,6 +1015,165 @@ }); }, + paste_node: function( path ) { + + let _this = this; + let overwrite = false; + + if( this.clipboard == '' ) { + + codiad.message.error( i18n( 'Nothing in Your Clipboard' ) ); + } else if( path == this.clipboard ) { + + codiad.message.error( i18n( 'Cannot Paste Directory Into Itself' ) ); + } else { + + let short_name = _this.get_short_name( _this.clipboard ); + let new_path = path + '/' + short_name + + if( $( '#file-manager a[data-path="' + new_path + '"]' ).length ) { + + // Confirm overwrite? + codiad.modal.load( 400, this.dialog, { + action: 'overwrite', + path: new_path + }); + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + codiad.modal.unload(); + + if( $( '#modal-content form select[name="or_action"]' ).val() == 1 ) { + + overwrite = true; + } + + $.ajax({ + type: 'POST', + url: _this.controller + '?action=copy', + data: { + + path: path, + destination: new_path, + overwrite: overwrite, + }, + success: async function( data ) { + + + amplify.publish( 'filemanager.onPaste', { + path: path, + shortName: shortName, + duplicate: duplicate + }); + + let dir = await _this.is_dir( new_path ); + + if( dir ) { + + _this.rescan( new_path ); + } else { + + _this.rescan( _this.get_parent( new_path ) ); + } + + if( path !== new_path ) { + + _this.rescan( path ); + } + }, + error: function( data ) { + + console.log( data ); + }, + }); + }); + } else { + + $.ajax({ + type: 'POST', + url: _this.controller + '?action=copy', + data: { + + path: path, + destination: new_path, + overwrite: overwrite, + }, + success: async function( data ) { + + + amplify.publish( 'filemanager.onPaste', { + path: path, + shortName: shortName, + duplicate: duplicate + }); + + let dir = await _this.is_dir( new_path ); + + if( dir ) { + + _this.rescan( new_path ); + } else { + + _this.rescan( _this.get_parent( new_path ) ); + } + + if( path !== new_path ) { + + _this.rescan( path ); + } + }, + error: function( data ) { + + console.log( data ); + }, + }); + } + } + }, + + refresh_preview: function( event ) { + + let _this = codiad.filemanager; + + /** + * When reloading after every change, we encounter performance issues + * in the editor. Therefore, we implement the same logic as the + * auto_save module where we only reload after the user has finished + * changing their document. + */ + + if( _this.refresh_interval !== null ) { + + clearTimeout( _this.refresh_interval ); + _this.refresh_interval = null; + } + _this.refresh_interval = setTimeout( function() { + + if( _this.preview == null ) { + + return; + } + + try { + + if( ( typeof _this.preview.location.reload ) == "undefined" ) { + + _this.preview = null; + codiad.editor.getActive().removeEventListener( "change", _this.refreshPreview ); + return; + } + _this.preview.location.reload( true ); + } catch ( e ) { + + console.log( e ); + codiad.message.error( 'Please close your previously opened preview window.' ); + _this.preview = null; + codiad.editor.getActive().removeEventListener( "change", _this.refreshPreview ); + } + }, 500 ); + }, + rename: function( path, new_path ) { let _this = codiad.filemanager; @@ -1083,11 +1271,148 @@ _this.index( path, true ); }, - save_file: function() {}, + save_file: function( path, data, display_messages = true ) { + + return new Promise( function( resolve, reject ) { + + let _this = codiad.filemanager; + + $.ajax({ + type: 'POST', + url: _this.controller + '?action=modify', + data: { + + path: path, + data: JSON.stringify( data ), + }, + success: function( data ) { + + console.log( data ); + let r = JSON.parse( data ); + + if( r.status === "success" ) { + + if( display_messages === true ) { + + codiad.message.success( i18n( 'File saved' ) ); + } + resolve( data ); + } else if( r.message == 'Client is out of sync' ) { + + let reload = confirm( + "Server has a more updated copy of the file. Would " + + "you like to refresh the contents ? Pressing no will " + + "cause your changes to override the server's copy upon " + + "next save." + ); + if( reload ) { + + codiad.active.close( path ); + codiad.active.removeDraft( path ); + _this.openFile( path ); + } else { + + let session = codiad.editor.getActive().getSession(); + session.serverMTime = null; + session.untainted = null; + } + resolve( data ); + } else { + + codiad.message.error( i18n( r.message ) ); + reject( data ); + } + }, + error: function( data ) { + + codiad.message.error( i18n( 'File could not be saved' ) ); + reject( data ); + }, + }); + }); + }, - save_modifications: function() {}, - - save_patch: function() {}, + search: async function( path ) { + + let _this = this; + let container = await codiad.modal.load( 500, this.dialog, { + action: 'search', + path: path + }); + codiad.modal.hideOverlay(); + let last_searched = JSON.parse( await codiad.settings.get_option( "lastSearched" ) ); + if( last_searched ) { + + $( '#modal-content form input[name="search_string"]' ).val( lastSearched.searchText ); + $( '#modal-content form input[name="search_file_type"]' ).val( lastSearched.fileExtension ); + $( '#modal-content form select[name="search_type"]' ).val( lastSearched.searchType ); + if( lastSearched.searchResults != '' ) { + $( '#filemanager-search-results' ).slideDown().html( lastSearched.searchResults ); + } + } + + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + $( '#filemanager-search-processing' ).show(); + e.preventDefault(); + searchString = $( '#modal-content form input[name="search_string"]' ).val(); + fileExtensions = $( '#modal-content form input[name="search_file_type"]' ).val(); + searchFileType = $.trim( fileExtensions ); + if( searchFileType != '' ) { + //season the string to use in find command + searchFileType = "\\(" + searchFileType.replace( /\s+/g, "\\|" ) + "\\)"; + } + searchType = $( '#modal-content form select[name="search_type"]' ) + .val(); + let options = { + filetype: fileExtensions, + }; + $.post( _this.controller + '?action=search', { + path: path, + query: searchString, + options: JSON.stringify( options ) + }, function( data ) { + + let searchResponse = codiad.jsend.parse( data ); + let results = ''; + + console.log( data ); + console.log( searchResponse ); + + if( searchResponse != 'error' ) { + $.each( searchResponse.index, function( key, val ) { + // Cleanup file format + if( val['file'].substr( -1 ) == '/' ) { + val['file'] = val['file'].substr( 0, str.length - 1 ); + } + val['file'] = val['file'].replace( '//', '/' ); + // Add result + results += '
Line ' + val['line'] + ': ' + val['file'] + '
'; + }); + $( '#filemanager-search-results' ) + .slideDown() + .html( results ); + } else { + $( '#filemanager-search-results' ) + .slideUp(); + } + + codiad.settings.update_option( + "lastSearched", + JSON.stringify({ + searchText: searchText, + searchType: searchType, + fileExtension: fileExtensions, + searchResults: searchResults + }) + ) + _this.saveSearchResults( searchString, searchType, fileExtensions, results ); + $( '#filemanager-search-processing' ) + .hide(); + }); + }); + }, set_children: function( path, files, children ) { @@ -1261,18 +1586,54 @@ upload: function() {}, - //Compatibility functions + upload_to_node: function( path ) { + + let _this = codiad.filemanager; + codiad.modal.load( + 500, + this.dialog, { + action: 'upload', + } + ) + .then( function( container ) { + + let text = $( '

' ); + let input = $( '' ); + + text.html( `

Drag and drop files or folders anywhere or click here to upload a file!

` ); + + input.on( 'change', function( e ) { + + console.log( e ); + + let items = e.target.files; + _this.upload( items, path ); + }); + text.on( 'click', function( e ) { + + input.click(); + }); + + container.html( '' ); + container.append( text ); + container.append( input ); + }); + }, - copyNode: this.copy_node, + //Compatibility functions + //these may be needed more after updating the new functions to newer standards + + copyNode: function( path ) {return this.copy_node( path )}, createNode: function( path, type ) {return this.create_node( path, type )}, - deleteNode: this.delete_node, + deleteNode: function( path ) {return this.delete_node( path )}, getExtension: function( path ) {return this.get_extension( path )}, getShortName: function( path ) {return this.get_short_name( path )}, getType: function( path ) {return this.get_type( path )}, openFile: function( path ) {return this.open_file( path )}, - openInBrowser: this.preview, - pasteNode: this.paste_node, - renameNode: this.rename_node, - saveFile: this.save_file, + openInBrowser: function( path ) {return this.preview( path )}, + pasteNode: function( path ) {return this.paste_node( path )}, + renameNode: function( path ) {return this.rename_node( path )}, + saveFile: function( path, data, display_messages ) {return this.save_file( path, data, display_messages )}, + uploadToNode: function( path ) {return this.upload_to_node( path )}, }; })( this, jQuery ); \ No newline at end of file diff --git a/js/modal.js b/js/modal.js index 141b67d..9961760 100755 --- a/js/modal.js +++ b/js/modal.js @@ -19,44 +19,44 @@ .hide(); }, - load: function( width, url, data, callback = null ) { + load: function( width, url, data ) { - data = data || {}; - let bounds = this._getBounds( width ); - let content = $( '#modal-content' ) - $('#modal') - .css({ - 'top': bounds.top, - 'left': bounds.left, - 'min-width': width + 'px', - 'margin-left': '-' + Math.ceil( width / 2 ) + 'px' - }) - .draggable({ - handle: '#drag-handle' - }); - content.html(''); - - this.load_process = $.get( url, data, function( data ) { + return new Promise( function( resolve, reject ) { - content.html( data ); - // Fix for Firefox autofocus goofiness - $('#modal-content input[autofocus="autofocus"]').focus(); + data = data || {}; + let _this = codiad.modal; + let bounds = _this._getBounds( width ); + let content = $( '#modal-content' ) + $('#modal') + .css({ + 'top': bounds.top, + 'left': bounds.left, + 'min-width': width + 'px', + 'margin-left': '-' + Math.ceil( width / 2 ) + 'px' + }) + .draggable({ + handle: '#drag-handle' + }); + content.html(''); - if( callback ) { + _this.load_process = $.get( url, data, function( data ) { - callback( content ); - } - }); - - let event = {animationPerformed: false}; - amplify.publish( 'modal.onLoad', event ); - - // If no plugin has provided a custom load animation - if( ! event.animationPerformed ) { + content.html( data ); + // Fix for Firefox autofocus goofiness + $('#modal-content input[autofocus="autofocus"]').focus(); + resolve( content ); + }).error( reject ); - $('#modal, #modal-overlay').fadeIn(200); - } - codiad.sidebars.modalLock = true; + let event = {animationPerformed: false}; + amplify.publish( 'modal.onLoad', event ); + + // If no plugin has provided a custom load animation + if( ! event.animationPerformed ) { + + $('#modal, #modal-overlay').fadeIn(200); + } + codiad.sidebars.modalLock = true; + }); }, show_loading: function() {