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 += ''; + }); + $( '#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( `