diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 517cd25..87a2fed 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,25 +1,27 @@ # How to Contribute -Your contributions are welcome and we're very open about how contributions are made, however, to keep order to things please take the following into consideration: +Your contributions are welcome and we're very open about how contributions are made; however, to keep order to things please take the following into consideration: * Check the issues to ensure that someone else isn't already working on the bug or feature * Submit an issue for bugs and feature additions before you start with it * Familiarize yourself with the documentation in the [Wiki](https://gitlab.com/xevidos/codiad/wikis/home) -There is an established format for `components` which utilizes one JS (`init.js`) and one CSS (`screen.css`) which is handled by the loader file. Any other resources used should be loaded or accessed from one of these. +**Merge Requests** All merge requests should be submitted for merging into the development branch. All tests and checks are run on that branch before merging into master to form a release. -**Don't Reinvent the Wheel!** There's an API and defined, easy-to-understand set of methods for a reason - use them. +There is an established format for `components` which utilizes one JS (`init.js`) and one CSS (`screen.css`) which is handled by the loader file. Any other resources used should be loaded or accessed from one of these. Plugins and Themes made for Codiad do not need to follow our formatting styles below, but any contributions to the core should. -Stick to the conventions defined in other components as closely as possible. +**Don't Reinvent the Wheel!** There's an API and a defined, easy-to-understand set of methods for a reason - use them. + +Stick to the conventions defined in other components as closely as possible. * Utilize the same commenting structure -* Use underscores in namespaces instead of interCaps -* Use intend with a tab character in your code +* Use underscores in namespaces instead of camelCase +* Indent using tabs * When working with the editor utilize the `active` object whenever possible instead of going direct to the `editor` **Javascript Formatting** -In order to maintain a consistant code structure to the code across the application please follow the wordpress standard, or run any changes through [JSBeautifier] (http://jsbeautifier.org/) with the settings below. +In order to maintain a consistent code structure to the code across the application, please follow the WordPress standard, or run any changes through [JSBeautifier] (http://jsbeautifier.org/) with the settings below. { "brace_style": "collapse", @@ -50,4 +52,4 @@ If you have questions, please ask. Submit an issue or [contact us directly](mail **PHP Formatting** -In order to maintain a consistant code structure we follow WordPress standards. +In order to maintain a consistent code structure we follow WordPress standards. diff --git a/README.md b/README.md index 8bbd19e..ca48880 100755 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ Current Tasks: Task List: +* Add ability to create shortlinks with permissions for users to share files or projects. * Add ability to login with LDAP +* Add bookmark files * Add custom market * \- Add in new admin interface ( Check admin-portal branch for progress ) - Group Management @@ -46,17 +48,24 @@ Task List: - Project Management - System Settings - User Management -* Add Drag and Drop natively to filemanager -* Add folder / filestructure upload ability +* Add different code linters * Add if file could not be saved 5 times close the open file * Add multi level users. ( Projects for only certain groups, Permission levels ) * Add mobile compatibility -* Add permissions module ( more in depth permissions such as read/write, delete, etc ) +* Add print code +* Add support for more archive types ( Add commands add more accepted PHP extension types ) * Add support for more database systems ( MSSQL, Oracle, SQLite, Filesystem storage, etc ) -* Add in auto save timer that saves after the user stops typing instead of after every change +* Add terminal support ( optional per permission level ) * Clean up update script +* Create standards for php ( For example a lot of projects are using API like standards for their backends maybe create something like those? ) * Fix broken themes * Re Add the custom language recognition system after recode +* Reformat install processes +* Remove all old and unneeded dependencies +* Seperate Upload filemanager instance from main filemanager instance +* Update all current components to use more current standards + - async await and .then in favor over callbacks in JS + - standards for php functions when created Completed: diff --git a/common.php b/common.php index f4a7d9e..bd489b5 100755 --- a/common.php +++ b/common.php @@ -5,6 +5,9 @@ * [root]/license.txt for more. This information must remain intact. */ +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +error_reporting(E_ALL); $sql = null; Common::startSession(); @@ -93,6 +96,18 @@ class Common { define( "LANGUAGE", "en" ); } + if( ! defined( 'UPLOAD_CACHE' ) ) { + + if( ! is_dir( sys_get_temp_dir() ) ) { + + define( "UPLOAD_CACHE", DATA . "/uploads" ); + } else { + + define( "UPLOAD_CACHE", rtrim( sys_get_temp_dir(), "/" ) ); + } + } + + require_once( COMPONENTS . "/permissions/class.permissions.php" ); require_once( COMPONENTS . "/update/class.update.php" ); require_once( COMPONENTS . "/sql/class.sql.php" ); global $sql; @@ -103,95 +118,46 @@ class Common { // New Methods ////////////////////////////////////////////////////////////////// - - - ////////////////////////////////////////////////////////////////// - // Check access to application - ////////////////////////////////////////////////////////////////// - - public static function check_access( $action = "return" ) { - - /*if( ! self::check_session() ) { - - session_destroy(); - self::return( formatJSEND( "error", "Error fetching project information." ), "exit" ); - }*/ - } - - ////////////////////////////////////////////////////////////////// - // Check access to a project - ////////////////////////////////////////////////////////////////// - public static function check_project_access( $project_path, $action ) { + public static function get_user_id( $username ) { global $sql; - $query = "SELECT * FROM projects WHERE name=? AND path=? AND ( owner=? OR owner='nobody' );"; - $bind_variables = array( $project_name, $project_path, $_SESSION["user"] ); - $return = $sql->query( $query, $bind_variables, formatJSEND( "error", "Error checking project access." ) ); + $user_id = false; + $query = "SELECT id FROM users WHERE username = ? LIMIT 1;"; + $bind_variables = array( $username ); + $return = $sql->query( $query, $bind_variables, array(), "fetch" ); if( ! empty( $return ) ) { - try { - - $users = json_decode( $return["access"] ); - } catch( exception $e ) { - - $users = array(); - } - - if( $return["owner"] == 'nobody' || $return["owner"] == $_SESSION["user"] || ( in_array( $_SESSION["user"], $users ) && ! empty( $users ) ) ) { - - $return = true; - } else { - - $return = false; - } - } else { - - $return = false; + $user_id = $return["id"]; } - - self::return( $return, $action ); + return $user_id; } public static function get_users( $return = "return", $exclude_current = false ) { global $sql; - $query = "SELECT username FROM users"; - $bind = ""; + $query = "SELECT * FROM users"; $bind_variables = array(); if( $exclude_current ) { - $query .= " WHERE username!=?"; - $bind .= "s"; + $query .= " WHERE username <> ?"; array_push( $bind_variables, $_SESSION["user"] ); } - $result = $sql->query( $query, $bind_variables, formatJSEND( "error", "Error checking users." ) ); - $user_list = array(); + $result = $sql->query( $query, $bind_variables, array() ); - foreach( $result as $row ) { + switch( $return ) { - array_push( $user_list, $row["username"] ); - } - - if( ! empty( $result ) ) { - - switch( $return ) { + case( "json" ): - case( "json" ): - - $return = json_encode( $user_list ); - break; - - case( "return" ): - - $return = $user_list; - break; - } - } else { + $return = json_encode( $result ); + break; - $return = formatJSEND( "error", "Error selecting user information." ); + case( "return" ): + + $return = $result; + break; } return( $return ); } @@ -204,10 +170,15 @@ class Common { public static function is_admin() { global $sql; - $query = "SELECT COUNT( * ) FROM users WHERE username=? AND access=?;"; - $bind_variables = array( $_SESSION["user"], "admin" ); - $return = $sql->query( $query, $bind_variables, -1, 'fetchColumn' ); - $admin = ( $return > 0 ); + $admin = false; + + if( isset( $_SESSION["user_id"] ) ) { + + $query = "SELECT COUNT( * ) FROM users WHERE id=? AND access=?;"; + $bind_variables = array( $_SESSION["user_id"], Permissions::SYSTEM_LEVELS["admin"] ); + $return = $sql->query( $query, $bind_variables, -1, 'fetchColumn' ); + $admin = ( $return > 0 ); + } return $admin; } @@ -302,8 +273,18 @@ class Common { Common::construct(); //Set a Session Name - session_name( md5( BASE_PATH ) ); - session_save_path( SESSIONS_PATH ); + try { + + session_name( md5( BASE_PATH ) ); + session_save_path( SESSIONS_PATH ); + } catch( exception $e ) { + } + + if( ! is_dir( SESSIONS_PATH ) ) { + + mkdir( SESSIONS_PATH ); + } + session_start(); if( ! defined( 'SESSION_ID' ) ) { @@ -328,7 +309,7 @@ class Common { } public static function return( $output, $action = "return" ) { - + switch( $action ) { case( "exit" ): @@ -353,32 +334,7 @@ class Common { public static function startSession() { - Common::construct(); - - //Set a Session Name - session_name( md5( BASE_PATH ) ); - session_save_path( SESSIONS_PATH ); - session_start(); - - if( ! defined( 'SESSION_ID' ) ) { - - define( "SESSION_ID", session_id() ); - } - - //Check for external authentification - if( defined( 'AUTH_PATH' ) ) { - - require_once( AUTH_PATH ); - } - - global $lang; - if ( isset( $_SESSION['lang'] ) ) { - - include BASE_PATH . "/languages/{$_SESSION['lang']}.php"; - } else { - - include BASE_PATH . "/languages/" . LANGUAGE . ".php"; - } + Common::start_session(); } ////////////////////////////////////////////////////////////////// @@ -452,11 +408,11 @@ class Common { $pass = false; - if( isset( $_SESSION["token"] ) && isset( $_SESSION["user"] ) ) { + if( isset( $_SESSION["token"] ) && isset( $_SESSION["user_id"] ) ) { global $sql; - $query = "SELECT COUNT( * ) FROM users WHERE username=? AND token=?;"; - $bind_variables = array( $_SESSION["user"], sha1( $_SESSION["token"] ) ); + $query = "SELECT COUNT( * ) FROM users WHERE id=? AND token=?;"; + $bind_variables = array( $_SESSION["user_id"], sha1( $_SESSION["token"] ) ); $return = $sql->query( $query, $bind_variables, formatJSEND( "error", "Error checking access." ), "fetchColumn" ); if( $return > 0 ) { @@ -520,33 +476,43 @@ class Common { // Format JSEND Response ////////////////////////////////////////////////////////////////// - public static function formatJSEND( $status, $data = false ) { + public static function formatJSEND( $status, $data = false, $debug = false ) { /// Debug ///////////////////////////////////////////////// - $debug = ""; + $jsend = array( + "status" => null, + "data" => null, + "debug" => null, + "message" => null, + ); + if( count( Common::$debugMessageStack ) > 0 ) { - $debug .= ',"debug":'; - $debug .= json_encode( Common::$debugMessageStack ); + $jsend["debug"] = json_encode( Common::$debugMessageStack ); + } + + if( $debug ) { + + $jsend["debug"] = $debug; } if( $status == "success" ) { // Success /////////////////////////////////////////////// + $jsend["status"] = "success"; + if( $data ) { - $jsend = '{"status":"success","data":' . json_encode( $data ) . $debug . '}'; - } else { - - $jsend = '{"status":"success","data":null' . $debug . '}'; + $jsend["data"] = $data; } } else { // Error ///////////////////////////////////////////////// - $jsend = '{"status":"' . $status . '","message":"' . $data . '"' . $debug . '}'; + $jsend["status"] = "error"; + $jsend["message"] = $data; } // Return //////////////////////////////////////////////// - return $jsend; + return json_encode( $jsend ); } ////////////////////////////////////////////////////////////////// @@ -564,47 +530,7 @@ class Common { public static function checkPath( $path ) { - global $sql; - //$query = "SELECT * FROM projects WHERE LOCATE( path, ? ) > 0 LIMIT 1;"; - //$bind_variables = array( $path ); - //$result = $sql->query( $query, $bind_variables, array() )[0]; - $result = $sql->select( - "projects", - array(), - array( - array( - "find", - "[path]", - $path, - array( - "more than", - 0 - ) - ), - array( - "limit", - 1 - ) - ) - ); - - if( ! empty( $result ) ) { - - $result = $result[0]; - try { - - $users = json_decode( $result["access"] ); - } catch( exception $e ) { - - $users = array(); - } - - if( $result["owner"] == 'nobody' || $result["owner"] == $_SESSION["user"] || ( in_array( $_SESSION["user"], $users ) && ! empty( $users ) ) ) { - - return( true ); - } - } - return( false ); + return Permissions::has_manager( $path ); } @@ -651,19 +577,20 @@ class Common { // Wrapper for old method names ////////////////////////////////////////////////////////////////// -function is_admin() { return Common::is_admin(); } -function debug($message) { Common::debug($message); } -function i18n($key, $args = array()) { echo Common::i18n($key, $args); } -function get_i18n($key, $args = array()) { return Common::get_i18n($key, $args); } -function checkSession(){ Common::checkSession(); } -function getJSON($file,$namespace=""){ return Common::getJSON($file,$namespace); } -function saveJSON($file,$data,$namespace=""){ Common::saveJSON($file,$data,$namespace); } -function formatJSEND($status,$data=false){ return Common::formatJSEND($status,$data); } function checkAccess() { return Common::checkAccess(); } -function checkPath($path) { return Common::checkPath($path); } -function isAvailable($func) { return Common::isAvailable($func); } -function logout() { return Common::logout(); } +function checkPath( $path ) { return Common::checkPath($path); } +function checkSession() { Common::checkSession(); } +function debug( $message ) { Common::debug( $message ); } +function formatJSEND( $status, $data=false ){ return Common::formatJSEND($status,$data); } +function get_i18n( $key, $args = array() ) { return Common::get_i18n($key, $args); } +function get_user_id( $username ) { return Common::get_user_id( $username ); } function get_users( $return = "return", $exclude_current = false ) { return Common::get_users( $return, $exclude_current ); } -function search_users( $username, $return = "return", $exclude_current = false ) { return Common::search_users( $username, $return, $exclude_current ); } function get_version() { return Common::get_version(); } +function getJSON( $file,$namespace=""){ return Common::getJSON( $file, $namespace ); } +function i18n( $key, $args = array() ) { echo Common::i18n( $key, $args ); } +function is_admin() { return Common::is_admin(); } +function isAvailable( $func ) { return Common::isAvailable( $func ); } +function logout() { return Common::logout(); } +function saveJSON( $file, $data, $namespace="" ){ Common::saveJSON( $file, $data, $namespace ); } +function search_users( $username, $return = "return", $exclude_current = false ) { return Common::search_users( $username, $return, $exclude_current ); } ?> diff --git a/components/active/class.active.php b/components/active/class.active.php index d3e0204..2dcb202 100755 --- a/components/active/class.active.php +++ b/components/active/class.active.php @@ -14,7 +14,6 @@ class Active extends Common { // PROPERTIES ////////////////////////////////////////////////////////////////// - public $username = ""; public $path = ""; public $new_path = ""; @@ -31,6 +30,17 @@ class Active extends Common { public function __construct() { } + public static function remove( $path ) { + + global $sql; + $query = array( + "*" => "DELETE FROM active WHERE path=? AND user=?;", + "pgsql" => 'DELETE FROM active WHERE path=? AND "user"=?;', + ); + $bind_variables = array( $path, $_SESSION["user_id"] ); + $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + } + ////////////////////////////////////////////////////////////////// // List User's Active Files ////////////////////////////////////////////////////////////////// @@ -38,14 +48,17 @@ class Active extends Common { public function ListActive() { global $sql; - $query = "SELECT path,position,focused FROM active WHERE username=?"; - $bind_variables = array( $this->username ); + $query = array( + "*" => "SELECT path, position, focused FROM active WHERE user=?", + "pgsql" => 'SELECT path, position, focused FROM active WHERE "user"=?', + ); + $bind_variables = array( $_SESSION["user_id"] ); $result = $sql->query( $query, $bind_variables, array() ); $tainted = false; $root = WORKSPACE; $active_list = $result; - if( ! empty( $return ) ) { + if( ! empty( $result ) ) { foreach ( $result as $id => $data ) { @@ -57,20 +70,14 @@ class Active extends Common { $root = $root.'/'; } - if ( ! file_exists( $root . $data['path'] ) ) { + if ( ! is_file( $root . $data['path'] ) ) { - $tainted = true; + self::remove( $data['path'] ); unset( $active_list[$id] ); } } } - - if( $tainted ) { - - $this->update_active( $active_list ); - } - - echo formatJSEND( "success", $active_list ); + exit( formatJSEND( "success", $active_list ) ); } ////////////////////////////////////////////////////////////////// @@ -80,7 +87,10 @@ class Active extends Common { public function Check() { global $sql; - $query = "SELECT username FROM active WHERE path=?"; + $query = array( + "*" => "SELECT user FROM active WHERE path=?", + "pgsql" => 'SELECT "user" FROM active WHERE path=?', + ); $bind_variables = array( $this->path ); $result = $sql->query( $query, $bind_variables, array() ); $tainted = false; @@ -90,10 +100,11 @@ class Active extends Common { foreach( $result as $id => $data ) { - array_push( $users, $data["username"] ); - if( $data["username"] == $this->username ) { + array_push( $users, $data["user"] ); + if( $data["user"] == $_SESSION ) { $user = true; + break; } } @@ -113,13 +124,37 @@ class Active extends Common { public function Add() { global $sql; - $query = "INSERT INTO active( username, path, focused ) VALUES ( ?, ?, ? );"; - $bind_variables = array( $this->username, $this->path, false ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + $query = array( + "*" => "SELECT focused FROM active WHERE path=? AND user=? LIMIT 1;", + "pgsql" => 'SELECT focused FROM active WHERE path=? AND "user"=? LIMIT 1;', + ); + $bind_variables = array( $this->path, $_SESSION["user_id"] ); + $result = $sql->query( $query, $bind_variables, array() ); - if( $return > 0 ) { + if( count( $result ) == 0 ) { - echo formatJSEND( "success" ); + $query = array( + "*" => "UPDATE active SET focused=false WHERE user=? AND path=?;", + "pgsql" => 'UPDATE active SET focused=false WHERE "user"=? AND path=?;', + ); + $bind_variables = array( $_SESSION["user_id"], $this->path ); + $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); + + if( $result == 0 ) { + + global $sql; + $query = array( + "*" => "INSERT INTO active( user, path, focused ) VALUES ( ?, ?, ? );", + "pgsql" => 'INSERT INTO active( "user", path, focused ) VALUES ( ?, ?, ? );', + ); + $bind_variables = array( $_SESSION["user_id"], $this->path, false ); + $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); + + if( $result > 0 ) { + + echo formatJSEND( "success" ); + } + } } } @@ -140,23 +175,6 @@ class Active extends Common { } } - ////////////////////////////////////////////////////////////////// - // Remove File - ////////////////////////////////////////////////////////////////// - - public function Remove() { - - global $sql; - $query = "DELETE FROM active WHERE path=? AND username=?;"; - $bind_variables = array( $this->path, $this->username ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - - if( $return > 0 ) { - - echo formatJSEND( "success" ); - } - } - ////////////////////////////////////////////////////////////////// // Remove All Files ////////////////////////////////////////////////////////////////// @@ -164,8 +182,11 @@ class Active extends Common { public function RemoveAll() { global $sql; - $query = "DELETE FROM active WHERE username=?;"; - $bind_variables = array( $this->username ); + $query = array( + "*" => "DELETE FROM active WHERE user=?;", + "pgsql" => 'DELETE FROM active WHERE "user"=?;', + ); + $bind_variables = array( $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $return > 0 ) { @@ -182,8 +203,11 @@ class Active extends Common { public function MarkFileAsFocused() { global $sql; - $query = "UPDATE active SET focused=? WHERE username=?;UPDATE active SET focused=? WHERE path=? AND username=?;"; - $bind_variables = array( false, $this->username, true, $this->path, $this->username ); + $query = array( + "*" => "UPDATE active SET focused=? WHERE path=? AND user=?;", + "pgsql" => 'UPDATE active SET focused=? WHERE path=? AND "user"=?;', + ); + $bind_variables = array( true, $this->path, $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $return > 0 ) { @@ -196,19 +220,19 @@ class Active extends Common { global $sql; $positions = json_decode( $positions, true ); - $query = ""; + $query = array( + "mysql" => "UPDATE active SET position=? WHERE path=? AND user=?;", + "pgsql" => 'UPDATE active SET position=? WHERE path=? AND "user"=?;', + ); $bind_variables = array(); if( json_last_error() == JSON_ERROR_NONE ) { foreach( $positions as $path => $cursor ) { - $query .= "UPDATE active SET position=? WHERE path=? AND username=?;"; - array_push( $bind_variables, json_encode( $cursor ), $path, $this->username ); + $return = $sql->query( $query, array( json_encode( $cursor ), $path, $_SESSION["user_id"] ), 0, "rowCount" ); } - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - if( $return > 0 ) { exit( formatJSEND( "success" ) ); diff --git a/components/active/controller.php b/components/active/controller.php index a5fa7ac..0d1f9ff 100755 --- a/components/active/controller.php +++ b/components/active/controller.php @@ -1,94 +1,101 @@ username = $_SESSION['user']; - $Active->ListActive(); -} - - ////////////////////////////////////////////////////////////////// - // Add active record - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='add') { - $Active->username = $_SESSION['user']; - $Active->path = $_GET['path']; - $Active->Add(); -} - - ////////////////////////////////////////////////////////////////// - // Rename - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='rename') { - $Active->username = $_SESSION['user']; - $Active->path = $_GET['old_path']; - $Active->new_path = $_GET['new_path']; - $Active->Rename(); -} - - ////////////////////////////////////////////////////////////////// - // Check if file is active - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='check') { - $Active->username = $_SESSION['user']; - $Active->path = $_GET['path']; - $Active->Check(); -} - - ////////////////////////////////////////////////////////////////// - // Remove active record - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='remove') { - $Active->username = $_SESSION['user']; - $Active->path = $_GET['path']; - $Active->Remove(); -} - - ////////////////////////////////////////////////////////////////// - // Remove all active record - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='removeall') { - $Active->username = $_SESSION['user']; - $Active->RemoveAll(); -} - - ////////////////////////////////////////////////////////////////// - // Mark file as focused - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='focused') { - $Active->username = $_SESSION['user']; - $Active->path = $_GET['path']; - $Active->MarkFileAsFocused(); -} - -if ($_GET['action']=='save_positions') { +if( $_GET['action'] == 'list' ) { - ignore_user_abort( true ); - $Active->username = $_SESSION['user']; - $Active->savePositions( $_POST["positions"] ); + $Active->username = $_SESSION['user']; + $Active->ListActive(); +} + +////////////////////////////////////////////////////////////////// +// Add active record +////////////////////////////////////////////////////////////////// + +if ( $_GET['action'] == 'add' ) { + + $Active->username = $_SESSION['user']; + $Active->path = $_GET['path']; + $Active->Add(); +} + +////////////////////////////////////////////////////////////////// +// Rename +////////////////////////////////////////////////////////////////// + +if ( $_GET['action'] == 'rename' ) { + + $Active->username = $_SESSION['user']; + $Active->path = $_GET['old_path']; + $Active->new_path = $_GET['new_path']; + $Active->Rename(); +} + +////////////////////////////////////////////////////////////////// +// Check if file is active +////////////////////////////////////////////////////////////////// + +if ( $_GET['action'] == 'check' ) { + + $Active->username = $_SESSION['user']; + $Active->path = $_GET['path']; + $Active->Check(); +} + +////////////////////////////////////////////////////////////////// +// Remove active record +////////////////////////////////////////////////////////////////// + +if ( $_GET['action'] == 'remove' ) { + + $Active->username = $_SESSION['user']; + $Active->path = $_GET['path']; + $Active->remove( $Active->path ); +} + +////////////////////////////////////////////////////////////////// +// Remove all active record +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'removeall' ) { + + $Active->username = $_SESSION['user']; + $Active->RemoveAll(); +} + +////////////////////////////////////////////////////////////////// +// Mark file as focused +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'focused' ) { + + $Active->username = $_SESSION['user']; + $Active->path = $_GET['path']; + $Active->MarkFileAsFocused(); +} + +if( $_GET['action'] == 'save_positions' ) { + + ignore_user_abort( true ); + $Active->username = $_SESSION['user']; + $Active->savePositions( $_POST["positions"] ); } diff --git a/components/active/init.js b/components/active/init.js index 1c5a035..102e58d 100755 --- a/components/active/init.js +++ b/components/active/init.js @@ -51,12 +51,8 @@ return !!this.sessions[path]; }, - open: function( path, content, mtime, inBackground, focus ) { + open: function( path, content, mtime, inBackground, focus, read_only=false ) { - //if( this. ) { - - - //} /* Notify listeners. */ amplify.publish( 'active.onFileWillOpen', { path: path, @@ -64,12 +60,14 @@ }); if( focus === undefined ) { + focus = true; } var _this = this; if( this.isOpen( path ) ) { + if( focus ) this.focus( path ); return; } @@ -98,6 +96,8 @@ session.serverMTime = mtime; _this.sessions[path] = session; session.untainted = content.slice( 0 ); + session.read_only = read_only; + if( !inBackground && focus ) { codiad.editor.setSession( session ); } @@ -113,8 +113,7 @@ }; // Assuming the mode file has no dependencies - $.loadScript( 'components/editor/ace-editor/mode-' + mode.name + '.js', - fn ); + $.loadScript( 'components/editor/ace-editor/mode-' + mode.name + '.js', fn ); }, init: function() { @@ -275,6 +274,7 @@ // Open saved-state active files on load $.get( _this.controller + '?action=list', function( data ) { + console.log( data ); var listResponse = codiad.jsend.parse( data ); if( listResponse !== null ) { $.each( listResponse, function( index, data ) { @@ -361,6 +361,9 @@ session.listThumb = listThumb; $( '#list-active-files' ).append( listThumb ); + + + /* If the tab list would overflow with the new tab. Move the * first tab to dropdown, then add a new tab. */ if( this.isTabListOverflowed( true ) ) { @@ -374,7 +377,10 @@ this.updateTabDropdownVisibility(); - $.get( this.controller + '?action=add&path=' + encodeURIComponent( path ) ); + $.get( this.controller + '?action=add&path=' + encodeURIComponent( path ), function( data ) { + + //console.log( data ); + }); if( focus ) { this.focus( path ); @@ -440,7 +446,7 @@ var session = this.sessions[path]; - if( $( '#dropdown-list-active-files' ).has( session.tabThumb ).length > 0 ) { + if( session && session.tabThumb && $( '#dropdown-list-active-files' ).has( session.tabThumb ).length > 0 ) { if( moveToTabList ) { /* Get the menu item as a tab, and put the last tab in * dropdown. */ @@ -494,11 +500,11 @@ .getSession(); var content = session.getValue(); var path = session.path; - var handleSuccess = function( mtime ) { + var handleSuccess = function( result ) { var session = codiad.active.sessions[path]; if( typeof session != 'undefined' ) { session.untainted = newContent; - session.serverMTime = mtime; + session.serverMTime = result.data.mtime; if( session.listThumb ) session.listThumb.removeClass( 'changed' ); if( session.tabThumb ) session.tabThumb.removeClass( 'changed' ); } @@ -516,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, {content: 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, {content: newContent}, alerts ) + .then( handleSuccess ); } }, @@ -551,9 +567,11 @@ ////////////////////////////////////////////////////////////////// remove: function( path ) { + + console.log( "remove file", this.isOpen( path ) ); if( !this.isOpen( path ) ) return; - var session = this.sessions[path]; - var closeFile = true; + let session = this.sessions[path]; + let closeFile = true; if( session.listThumb.hasClass( 'changed' ) ) { codiad.modal.load( 450, 'components/active/dialog.php?action=confirm&path=' + encodeURIComponent( path ) ); closeFile = false; @@ -745,14 +763,18 @@ ////////////////////////////////////////////////////////////////// getSelectedText: function() { - var path = this.getPath(); - var session = this.sessions[path]; + + let path = this.getPath(); + let session = this.sessions[path]; if( path && this.isOpen( path ) ) { - return session.getTextRange( - codiad.editor.getActive() - .getSelectionRange() ); + + console.log( "Session:", session ); + console.log( "selection: ", codiad.editor.getActive().getSelectionRange() ) + console.log( "text: ", session.getTextRange( codiad.editor.getActive().getSelectionRange() ) ) + return session.getTextRange( codiad.editor.getActive().getSelectionRange() ); } else { + codiad.message.error( i18n( 'No Open Files or Selected Text' ) ); } }, @@ -1013,14 +1035,17 @@ uploadPositions: function() { - $.ajax( { - type: 'POST', - url: codiad.active.controller + '?action=save_positions', - data: { - positions: ( JSON.stringify( codiad.active.positions ) ) - }, - success: function( data ) {}, - }); + if( Object.keys( codiad.active.positions ).length > 0 ) { + + $.ajax( { + type: 'POST', + url: codiad.active.controller + '?action=save_positions', + data: { + positions: ( JSON.stringify( codiad.active.positions ) ) + }, + success: function( data ) {}, + }); + } }, savePosition: function() { 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/editor/dialog.php b/components/editor/dialog.php index 3544bda..c64e597 100755 --- a/components/editor/dialog.php +++ b/components/editor/dialog.php @@ -19,6 +19,17 @@ checkSession(); + + + + + - diff --git a/components/editor/init.js b/components/editor/init.js index ba78c8b..1d9fa10 100755 --- a/components/editor/init.js +++ b/components/editor/init.js @@ -297,7 +297,7 @@ let path = codiad.active.getPath(); $( e.target ).attr( 'data-path', path ); - codiad.filemanager.contextMenuShow( e, path, 'editor', 'editor' ); + codiad.filemanager.display_context_menu( e, path, 'editor', 'editor' ); $( this ).addClass( 'context-menu-active' ); } }); @@ -390,11 +390,16 @@ this.setTabSize( this.settings.tabSize, i ); this.setSoftTabs( this.settings.softTabs, i ); this.setOverScroll( this.settings.overScroll, i ); - i.setOptions( { + i.setOptions({ enableBasicAutocompletion: true, enableSnippets: true, enableLiveAutocompletion: this.settings.autocomplete }); + + if( i.getSession().read_only ) { + + i.setReadOnly( true ); + } }, ////////////////////////////////////////////////////////////////// @@ -718,11 +723,15 @@ ///////////////////////////////////////////////////////////////// setSession: function( session, i ) { + i = i || this.getActive(); if( !this.isOpen( session ) ) { + if( !i ) { + i = this.addInstance( session ); } else { + i.setSession( session ); } } else { @@ -1287,7 +1296,12 @@ ' · ' + i18n( 'Col' ) + ': ' + i.getCursorPosition().column ); - + amplify.publish( 'editor.changeCursor', { + i: i, + row: i.getCursorPosition().row, + column: i.getCursorPosition().column, + }); + //Register the changecursor function so updates continue i.selection.on( "changeCursor", function( e ) { @@ -1298,6 +1312,12 @@ ' · ' + i18n( 'Col' ) + ': ' + i.getCursorPosition().column ); + + amplify.publish( 'editor.changeCursor', { + i: i, + row: i.getCursorPosition().row, + column: i.getCursorPosition().column, + }); }); }, @@ -1312,54 +1332,32 @@ bindKeys: function( i ) { + //Add key bindings to editor so we overwrite any already Setup + //by the ace editor. var _this = this; - // Find - i.commands.addCommand( { - name: 'Find', - bindKey: { - win: 'Ctrl-F', - mac: 'Command-F' - }, - exec: function( e ) { - _this.openSearch( 'find' ); - } + codiad.keybindings.bindings.forEach( function( m, j, a ) { + + i.commands.addCommand( m ); }); + }, + + open_goto: function() { - // Find + Replace - i.commands.addCommand( { - name: 'Replace', - bindKey: { - win: 'Ctrl-R', - mac: 'Command-R' - }, - exec: function( e ) { - _this.openSearch( 'replace' ); - } - }); - - i.commands.addCommand( { - name: 'Move Up', - bindKey: { - win: 'Ctrl-up', - mac: 'Command-up' - }, - exec: function( e ) { - codiad.active.move( 'up' ); - } - }); - - i.commands.addCommand( { - name: 'Move Down', - bindKey: { - win: 'Ctrl-down', - mac: 'Command-up' - }, - exec: function( e ) { - codiad.active.move( 'down' ); - } - }); + if( this.getActive() ) { + + codiad.modal.load( 400, 'components/editor/dialog.php?action=line' ); + codiad.modal.hideOverlay(); + } else { + codiad.message.error( 'No Open Files' ); + } + }, + + goto_line: function() { + let line = $( '#modal input[name="goto_line"]' ).val(); + this.gotoLine( line ); + codiad.modal.unload(); }, ////////////////////////////////////////////////////////////////// @@ -1372,13 +1370,34 @@ ////////////////////////////////////////////////////////////////// openSearch: function( type ) { + if( this.getActive() ) { - codiad.modal.load( 400, - 'components/editor/dialog.php?action=search&type=' + - type ); - codiad.modal.hideOverlay(); - } else { - codiad.message.error( 'No Open Files' ); + + let selected = codiad.active.getSelectedText(); + + codiad.modal.load( + 400, + 'components/editor/dialog.php?action=search&type=' + type, + {}, + ) + .then( function( c ) { + + let input = c.find( 'input:first' ); + let textarea = c.find( 'textarea:first' ); + + if( input.css( 'display' ) !== 'none' ) { + + input.val( selected ) + input.focus(); + } else if( textarea.css( 'display' ) !== 'none' ) { + + textarea.val( selected ) + textarea.focus(); + } + console.log( input, textarea ); + codiad.modal.hideOverlay(); + } + ); } }, @@ -1408,6 +1427,9 @@ var replace = $( '#modal input[name="replace"]' ) .val(); } + + console.log( action, i, find, replace ); + switch ( action ) { case 'find': @@ -1556,9 +1578,23 @@ openSort: function() { - if( this.getActive() && codiad.active.getSelectedText() != "" ) { + let selected = codiad.active.getSelectedText(); + + if( this.getActive() && selected != "" ) { - codiad.modal.load( 400, 'components/editor/dialog.php?action=sort' ); + codiad.modal.load( + 400, + 'components/editor/dialog.php?action=sort', + {} + ) + .then( function( c ) { + + let textarea = c.find( 'textarea:first' ); + + textarea.val( selected ) + textarea.focus(); + codiad.modal.hideOverlay(); + }); codiad.modal.hideOverlay(); } else { diff --git a/components/filemanager/class.archive.php b/components/filemanager/class.archive.php new file mode 100644 index 0000000..f4233e4 --- /dev/null +++ b/components/filemanager/class.archive.php @@ -0,0 +1,308 @@ + array( + "compress" => array( + "windows" => "", + "linux" => "zip -r9 %output% %input%", + ), + "decompress" => array( + "windows" => "", + "linux" => "unzip %input%", + ), + ), + ); + + const EXTENSIONS = array( + + "zip" => "zip", + ); + + const INVALID_FILES = array( + ".", + "..", + ".DS_Store" + ); + + const MIME_TYPE_EXTENSIONS = array( + + "application/zip" => "zip", + ); + + const SUPPORTED_TYPES = array( + + //"gz", + //"rar", + //"tar", + //"tar.gz", + "zip", + ); + + public static $instance = null; + public $manager = null; + + + function __construct() { + + + } + + public static function get_instance() { + + if ( null == self::$instance ) { + + self::$instance = new self; + } + + return self::$instance; + } + + public static function get_system() { + + $system = "unknown"; + + if( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) { + + $system = "windows"; + } else { + + $system = "linux"; + } + return $system; + } + + public static function compress( $path, $output = "default", $type = "default" ) { + + $response = array(); + + if( $type == "default" ) { + + $type = self:: get_supported_type(); + } + + if( $output == "default" ) { + + $output = dirname( $path ) . "/" . basename( $path ) . ".$type"; + $path_parts = pathinfo( $output ); + $existing = $output; + $i = 1; + + do { + + if( is_dir( $existing ) ) { + + $existing = rtrim( $output, "/" ) . " $i/"; + } elseif( is_file( $existing ) ) { + + if( isset( $path_parts["extension"] ) ) { + + $existing = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $output ); + } else { + + $existing = $output . " $i"; + } + } + $i++; + } while( is_file( $existing ) || is_dir( $existing ) ); + + $output = $existing; + } + + $supported = self::supports( $type ); + $archive = self::get_instance(); + + if( $supported["status"] === "success" ) { + + if( extension_loaded( self::EXTENSIONS["{$type}"] ) ) { + + $response = call_user_func( array( $archive, "{$type}_c" ), $path, $output ); + } else { + + //$response = $archive->execute( $type, "compress", $path, dirname( $path ) ); + } + } else { + + $response = $supported; + } + return $response; + } + + public static function decompress( $file, $output = "default" ) { + + $response = array(); + $path_info = pathinfo( $file ); + $type = isset( $path_info["extension"] ) ? $path_info["extension"] : null; + $supported = self::supports( $type ); + $archive = self::get_instance(); + + if( $output == "default" ) { + + $output = $path_info["dirname"] . "/" . $path_info["filename"]; + } + + if( $supported["status"] === "success" ) { + + if( extension_loaded( self::EXTENSIONS["{$type}"] ) ) { + + $response = call_user_func( array( $archive, "{$type}_d" ), $file, $output ); + } else { + + $response = $archive->execute( $type, "decompress" ); + } + + if( $response === true ) { + + $response = array( + "status" => "success", + "message" => null, + ); + } + } else { + + $response = $supported; + } + return $response; + } + + public static function get_supported_type() { + + //zip is usually the most used format supported by the most OS's, + //we check that first then check the rest of the types. + + $supported_type = null; + $types = self::SUPPORTED_TYPES; + $zip_id = array_search( "zip", $types ); + unset( $types[$zip_id] ); + array_unshift( $types, "zip" ); + + foreach( $types as $id => $type ) { + + if( self::supports( $type ) ) { + + $supported_type = $type; + break; + } + } + return $supported_type; + } + + public static function supports( $type ) { + + $response = array(); + $type = strtolower( $type ); + + if( in_array( $type, self::SUPPORTED_TYPES ) ) { + + $system = self::get_system(); + $supported = false; + $extension = self::EXTENSIONS["{$type}"]; + + if( extension_loaded( $extension ) ) { + + $type_supported = true; + } elseif( isset( self::COMMANDS["{$type}"] ) && isset( self::COMMANDS["{$type}"]["compress"][$system] ) ) { + + $type_supported = true; + } + + if( $type_supported ) { + + $response["status"] = "success"; + $response["message"] = "Type is supported"; + } else { + + $response["status"] = "error"; + $response["message"] = "The extension or program required to use this type of file does not seem to be installed."; + } + } else { + + $response["status"] = "error"; + $response["message"] = "The filetype supplied is not currently supported by Codiad's archive management system."; + } + return $response; + } + + function zip_c( $path, $output, &$archive = null ) { + + if( $archive == null ) { + + $path = rtrim( $path, '/' ); + //$output = rtrim( $output, '/' ) . '/'; + $archive = new ZipArchive(); + + if( file_exists( $output ) ) { + + $result = $archive->open( $output, ZIPARCHIVE::OVERWRITE ); + } else { + + $result = $archive->open( $output, ZIPARCHIVE::CREATE ); + } + + if( $result !== true ) { + + return false; + } + + if( is_file( $path ) ) { + + $archive->addFile( $path, basename( $path ) ); + $archive->close(); + return true; + } + } + + $i = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) ); + + foreach( $i as $file ) { + + $file_name = $file->getBasename(); + $file_path = $file->getPathname(); + $relative_path = str_replace( $path, "", $file_path ); + $relative_path = ltrim( $relative_path, '/' ); + + if( in_array( $file_name, self::INVALID_FILES ) ) { + + continue; + } + + if( is_file( $file_path ) ) { + + $archive->addFile( $file_path, $relative_path ); + } else { + + $archive->addEmptyDir( $relative_path ); + $this->zip_c( $file_path, $output, $archive ); + } + } + $archive->close(); + + return true; + } + + function zip_d( $path, $output ) { + + if( ! is_dir( $output ) ) { + + mkdir( $output ); + } + + $status = false; + $output = rtrim( $output, '/' ) . '/'; + $archive = new ZipArchive(); + $open = $archive->open( $path ); + + if ( $open === true ) { + + $archive->extractTo( $output ); + $archive->close(); + $status = true; + } + return $status; + } +} + + +?> \ No newline at end of file diff --git a/components/filemanager/class.dirzip.php b/components/filemanager/class.dirzip.php deleted file mode 100755 index 3727463..0000000 --- a/components/filemanager/class.dirzip.php +++ /dev/null @@ -1,55 +0,0 @@ -addFile($filePath, $localPath); - } elseif (is_dir($filePath)) { - // Add sub-directory. - $zipFile->addEmptyDir($localPath); - self::folderToZip($filePath, $zipFile, $exclusiveLength); - } - } - } - closedir($handle); - } - - /** - * Zip a folder (include itself). - * Usage: - * DirZip::zipDir('/path/to/sourceDir', '/path/to/out.zip'); - * - * @param string $sourcePath Path of directory to be zip. - * @param string $outZipPath Path of output zip file. - */ - public static function zipDir($sourcePath, $outZipPath) - { - $pathInfo = pathInfo($sourcePath); - $parentPath = $pathInfo['dirname']; - $dirName = $pathInfo['basename']; - - $z = new ZipArchive(); - $z->open($outZipPath, ZIPARCHIVE::CREATE); - $z->addEmptyDir($dirName); - self::folderToZip($sourcePath, $z, strlen("$parentPath/")); - $z->close(); - } -} diff --git a/components/filemanager/class.filemanager.php b/components/filemanager/class.filemanager.php index 8946de7..703a9b1 100755 --- a/components/filemanager/class.filemanager.php +++ b/components/filemanager/class.filemanager.php @@ -1,43 +1,23 @@ rel_path = Filemanager::cleanPath($get['path']); + public function __construct() {} + + ////////////////////////////////////////////////////////////////// + // Clean a path + ////////////////////////////////////////////////////////////////// + + public static function cleanPath( $path ) { - if ( $this->rel_path != "/ ") { + // Prevent going out of the workspace + while ( strpos( $path, '../' ) !== false ) { - $this->rel_path .= "/"; + $path = str_replace( '../', '', $path ); } - if ( ! empty( $get['query'] ) ) { - $this->query = $get['query']; - } - if ( ! empty($get['options'] ) ) { - $this->foptions = $get['options']; - } - $this->root = $get['root']; - if ( $this->isAbsPath( $get['path'] ) ) { + + if( self::isAbsPath( $path ) ) { - $this->path = Filemanager::cleanPath($get['path']); + $full_path = $path; } else { - $this->root .= '/'; - $this->path = $this->root . Filemanager::cleanPath( $get['path'] ); - } - // Search - if ( ! empty( $post['search_string'] ) ) { - $this->search_string = ($post['search_string']); - } - if ( ! empty( $post['search_file_type'] ) ) { - $this->search_file_type = ($post['search_file_type']); - } - // Create - if ( ! empty($get['type'] ) ) { - - $this->type = $get['type']; - } - // Modify\Create - if ( ! empty( $get['new_name'] ) ) { - - $this->new_name = $get['new_name']; + $full_path = WORKSPACE . "/" . $path; } - foreach ( array( 'content', 'mtime', 'patch' ) as $key ) { + /** + * If a file with an invalid character exists and the user is + * trying to rename or delete it, allow the actual file name. + */ + + $invalid_characters = preg_match( '/[^A-Za-z0-9\-\._@\/\(\) ]/', $path ); + + if( $invalid_characters && ! ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - if ( ! empty( $post[$key] ) ) { + exit( formatJSEND( "error", "Error, the filename contains invalid characters, please either rename or delete it." ) ); + } elseif( $invalid_characters && ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { + } else { + + $path = preg_replace( '/[^A-Za-z0-9\-\._@\/\(\) ]/', '', $path ); + } + return $path; + } + + ////////////////////////////////////////////////////////////////// + // DUPLICATE (Creates a duplicate of the object - (cut/copy/paste) + ////////////////////////////////////////////////////////////////// + + public function copy( $source, $destination, $replace = false ) { + + $response = array( + "status" => "none", + "message" => null, + ); + + $source = self::formatPath( $source ); + $destination = self::formatPath( $destination ); + $new_destination = $destination; + $path_parts = pathinfo( $destination ); + $i = 1; + + if( ! $replace ) { + + do { - if ( get_magic_quotes_gpc() ) { + if( is_dir( $new_destination ) ) { - $this->$key = stripslashes( $post[$key] ); + $new_destination = rtrim( $destination, "/" ) . " $i/"; + } elseif( is_file( $new_destination ) ) { + + if( isset( $path_parts["extension"] ) ) { + + $new_destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $destination ); + } else { + + $new_destination = $destination . " $i"; + } + } + $i++; + } while( ( is_file( $new_destination ) || is_dir( $new_destination ) ) ); + } + + if( file_exists( $source ) ) { + + if( is_file( $source ) ) { + + copy( $source, $new_destination ); + $response["status"] = "success"; + } else { + + self::recursive_copy( $source, $new_destination ); + $response["status"] = "success"; + } + } else { + + $response["status"] = "error"; + $response["message"] = "Invalid Source"; + } + return $response; + } + + ////////////////////////////////////////////////////////////////// + // CREATE (Creates a new file or directory) + ////////////////////////////////////////////////////////////////// + + public function create( $path, $type, $content = "" ) { + + $response = array( + "status" => "none", + "message" => null, + ); + $path = self::formatPath( $path ); + + if( Permissions::has_create( $path ) ) { + + // Create file + if( $type == "file" ) { + + if ( ! file_exists( $path ) ) { + + if ( $file = fopen( $path, 'w' ) ) { + + // Write content + if ( $content ) { + + fwrite( $file, $content ); + } + fclose( $file ); + + $response["status"] = "success"; + $response["mtime"] = filemtime( $path ); + } else { + + $response["status"] = "error"; + $response["message"] = "Cannot Create File"; + } } else { - $this->$key = $post[$key]; + $response["status"] = "error"; + $response["message"] = "File Already Exists"; } - } - } - // Duplicate - if ( ! empty( $get['destination'] ) ) { - - $get['destination'] = Filemanager::cleanPath( $get['destination'] ); - if ( $this->isAbsPath( $get['path'] ) ) { + } elseif( $type == "directory" ) { - $i = 1; - $this->destination = $get['destination']; - - do { + if ( ! is_dir( $path ) ) { - if( is_dir( $this->destination ) ) { - - $this->destination = $get['destination'] . " $i"; - } elseif( is_file( $this->destination ) ) { - - $path_parts = pathinfo( $this->destination ); - - if( isset( $path_parts["extension"] ) ) { - - $this->destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $get['destination'] ); - } else { - - $this->destination = $get['destination'] . " $i"; - } - } + mkdir( $path ); + $response["status"] = "success"; + } else { - $i++; - } while( ( is_file( $this->destination ) || is_dir( $this->destination ) ) ); - - } else { - - $i = 1; - $this->destination = $this->root . $get['destination']; - do { - - if( is_dir( $this->destination ) ) { - - $this->destination = $this->root . $get['destination'] . " $i"; - } elseif( is_file( $this->destination ) ) { - - $path_parts = pathinfo( $this->destination ); - - if( isset( $path_parts["extension"] ) ) { - - $this->destination = str_replace( ".{$path_parts["extension"]}", " {$i}.{$path_parts["extension"]}", $this->root . $get['destination'] ); - } else { - - $this->destination = $this->root . $get['destination'] . " $i"; - } - } - - $i++; - } while( ( is_file( $this->destination ) || is_dir( $this->destination ) ) ); - } - } - } - - ////////////////////////////////////////////////////////////////// - // INDEX (Returns list of files and directories) - ////////////////////////////////////////////////////////////////// - - public function index() { - - if ( file_exists( $this->path ) ) { - - $index = array(); - if ( is_dir( $this->path ) && $handle = opendir( $this->path ) ) { - - while (false !== ( $object = readdir( $handle ) ) ) { - - if ($object != "." && $object != ".." && $object != $this->controller) { - if ( is_dir( $this->path.'/'.$object ) ) { - - $type = "directory"; - $size = count( glob( $this->path . '/' . $object . '/*' ) ); - } else { - - $type = "file"; - $size = @filesize( $this->path.'/' . $object ); - } - $index[] = array( - "name"=>$this->rel_path . $object, - "type"=>$type, - "size"=>$size - ); - } + $response["status"] = "error"; + $response["message"] = "Directory Already Exists"; } - - $folders = array(); - $files = array(); - foreach ($index as $item => $data) { - - if ( $data['type'] == 'directory' ) { - - $folders[] = array( "name"=>htmlentities( $data['name'], ENT_QUOTES ), "type"=>$data['type'], "size"=>$data['size'] ); - } - if ( $data['type'] == 'file' ) { - - $files[] = array( "name"=>htmlentities( $data['name'], ENT_QUOTES ), "type"=>$data['type'], "size"=>$data['size'] ); - } - } - - function sorter($a, $b, $key = 'name') { - - return strnatcmp( $a[$key], $b[$key] ); - } - - usort( $folders, "sorter" ); - usort( $files, "sorter" ); - - $output = array_merge( $folders, $files ); - - $this->status = "success"; - $this->data = '"index":' . json_encode( $output ); - } else { - - $this->status = "error"; - $this->message = "Not A Directory"; } } else { - $this->status = "error"; - $this->message = "Path Does Not Exist"; + + $response["status"] = "error"; + $response["message"] = "You do not have permission to create files in this project"; } - - $this->respond(); + return $response; } - public function find() { + ////////////////////////////////////////////////////////////////// + // DELETE (Deletes a file or directory (+contents or only contents)) + ////////////////////////////////////////////////////////////////// + + public function delete( $path, $follow, $keep_parent = false ) { + + $response = array( + "status" => "none", + "message" => null, + ); + + if( ! Common::checkPath( $path ) ) { + $response["status"] = "error"; + $response["message"] = "You do not have access to delete this file"; + } else { + + $path = self::formatPath( $path ); + if ( file_exists( $path ) ) { + + self::recursive_delete( $path, $follow, $keep_parent ); + $response["status"] = "success"; + } else { + + $response["status"] = "error"; + $response["message"] = "Path Does Not Exist "; + } + } + return $response; + } + + public function find( $path, $query, $options = array() ) { + + $response = array( + "status" => "none", + "message" => null, + ); + $current_path = getcwd(); + $path = self::formatPath( $path ); + if ( ! function_exists( 'shell_exec' ) ) { - $this->status = "error"; - $this->message = "Shell_exec() Command Not Enabled."; + $response["status"] = "error"; + $response["message"] = "Shell_exec() Command Not Enabled."; } else { - chdir( $this->path ); - $input = str_replace( '"', '', $this->query ); + chdir( $path ); + $input = str_replace( '"', '', $query ); $cmd = 'find -L '; $strategy = ''; - if ( $this->foptions && $this->foptions['strategy'] ) { + if ( ! empty( $options ) && isset( $options["strategy"] ) ) { - $strategy = $this->foptions['strategy']; + $strategy = $options["strategy"]; } + switch ( $strategy ) { case 'substring': - $cmd = "$cmd -iname ".escapeshellarg( '*' . $input . '*' ); + + $cmd = "$cmd -iname " . escapeshellarg( '*' . $input . '*' ); break; case 'regexp': - $cmd = "$cmd -regex ".escapeshellarg( $input ); + + $cmd = "$cmd -regex " . escapeshellarg( $input ); break; case 'left_prefix': default: - $cmd = "$cmd -iname ".escapeshellarg( $input . '*'); + $cmd = "$cmd -iname " . escapeshellarg( $input . '*'); break; } $cmd = "$cmd -printf \"%h/%f %y\n\""; @@ -278,7 +275,7 @@ class Filemanager extends Common { } if ( strlen( $fname ) != 0 ) { - $fname = $this->rel_path . substr( $fname, 2 ); + $fname = $path . substr( $fname, 2 ); $f = array( 'path' => $fname, 'type' => $ftype ); array_push( $output_arr, $f ); } @@ -286,73 +283,297 @@ class Filemanager extends Common { if ( count( $output_arr ) == 0 ) { - $this->status = "error"; - $this->message = "No Results Returned"; + $response["status"] = "error"; + $response["message"] = "No Results Returned"; } else { - $this->status = "success"; - $this->data = '"index":' . json_encode( $output_arr ); + $response["status"] = "success"; + $response["index"] = $output_arr; } } - $this->respond(); + return $response; + } + + public static function formatPath( $path ) { + + if( self::isAbsPath( $path ) ) { + + $path = self::cleanPath( $path ); + } else { + + $path = WORKSPACE . "/" . self::cleanPath( $path ); + } + + if( is_dir( $path ) ) { + + $path = rtrim( $path, '/' ) . '/'; + } + return( $path ); } ////////////////////////////////////////////////////////////////// - // SEARCH + // INDEX (Returns list of files and directories) ////////////////////////////////////////////////////////////////// - public function search() { + public function index( $path ) { - if ( ! function_exists( 'shell_exec' ) ) { + $response = array( + "status" => "none", + "message" => null, + ); + $relative_path = rtrim( self::cleanPath( $path ), '/' ) . '/'; + $path = self::formatPath( $path ); + + if( file_exists( $path ) ) { - $this->status = "error"; - $this->message = "Shell_exec() Command Not Enabled."; - } else { + $index = array(); + + if( is_dir( $path ) ) { - if ( $_GET['type'] == 1 ) { + $files = $this->index_path( $path ); - $this->path = WORKSPACE; - } - $return = array(); - $input = str_replace( '"', '', $this->search_string ); - $cmd = 'find -L ' . escapeshellarg( $this->path ) . ' -iregex '.escapeshellarg( '.*' . $this->search_file_type ) . ' -type f | xargs grep -i -I -n -R -H ' . escapeshellarg( $input ) . ''; - $output = shell_exec( $cmd ); - $output_arr = explode( "\n", $output ); - foreach ( $output_arr as $line ) { - - $data = explode( ":", $line ); - $da = array(); - if ( count( $data ) > 2 ) { - - $da['line'] = $data[1]; - $da['file'] = str_replace( $this->path, '', $data[0] ); - $da['result'] = str_replace( $this->root, '', $data[0] ); - $da['string'] = str_replace( $data[0] . ":" . $data[1] . ':', '', $line ); - $return[] = $da; - } - } - if ( count( $return ) == 0 ) { - - $this->status = "error"; - $this->message = "No Results Returned"; + $response["status"] = "success"; + $response["data"] = array( "index" => $files ); } else { - $this->status = "success"; - $this->data = '"index":' . json_encode( $return ); + $response["status"] = "error"; + $response["message"] = "Not A Directory"; + } + } else { + + $response["status"] = "error"; + $response["message"] = "Path Does Not Exist"; + } + return $response; + } + + function index_path( $path ) { + + $paths = array(); + + if( is_dir( $path ) && $handle = opendir( $path ) ) { + + while( false !== ( $f = readdir( $handle ) ) ) { + + if ( "$f" != '.' && "$f" != '..' ) { + + $p = "$path" . DIRECTORY_SEPARATOR . "$f"; + $p = str_replace( "//", "/", $p ); + $rp = realpath( $p ); + $path_info = pathinfo( $p ); + + if( is_dir( $p ) ) { + + $children = $this->is_empty( $p ) ? null : array(); + + $paths[] = array( + + "basename" => $path_info["basename"], + "children" => $children, + "dirname" => str_replace( WORKSPACE . "/", "", $p ), + "extension" => null, + "filename" => $path_info["filename"], + "full_dirname" => $path_info["dirname"], + "full_path" => $p, + "path" => str_replace( WORKSPACE . "/", "", $p ), + "type" => "directory", + ); + } else { + + $paths[] = array( + "basename" => $path_info["basename"], + "dirname" => str_replace( WORKSPACE . "/", "", $p ), + "extension" => isset( $path_info["extension"] ) ? $path_info["extension"] : null, + "filename" => $path_info["filename"], + "path" => str_replace( WORKSPACE . "/", "", $p ), + "type" => "file", + ); + } + } } } - $this->respond(); + + closedir( $handle ); + usort( $paths, array( $this, "sorter" ) ); + return $paths; + } + + function is_empty( $dir ) { + + $pass = true; + + if( is_dir( $dir ) ) { + + $handle = opendir( $dir ); + while( false !== ( $entry = readdir( $handle ) ) ) { + + if( $entry != "." && $entry != ".." ) { + + $pass = false; + break; + } + } + closedir( $handle ); + } + return $pass; + } + + function sorter( $a, $b ) { + + $basename = strnatcmp( $a["basename"], $b["basename"] ); + $type = strnatcmp( $a["type"], $b["type"] ); + $result = 0; + + if( $type == 0 ) { + + $result = $basename; + } else { + + $result = $type; + } + + return $result; + } + + + ////////////////////////////////////////////////////////////////// + // MODIFY (Modifies a file name/contents or directory name) + ////////////////////////////////////////////////////////////////// + + public function modify( $path, $content, $patch = false, $mtime = 0 ) { + + // Change content + $response = array( + "status" => "none", + "message" => null, + ); + $path = self::formatPath( $path ); + + if( $content == ' ' ) { + + $content = ''; // Blank out file + } + + if( ! Permissions::has_write( $path ) ) { + + $response["status"] = "error"; + $response["message"] = "You do not have access to write to this file."; + return $response; + } + + if( $patch && ! $mtime ) { + + $response["status"] = "error"; + $response["message"] = "invalid mtime parameter not found"; + $response["mtime"] = $mtime; + return $response; + } + + if( is_file( $path ) ) { + + $serverMTime = filemtime( $path ); + $fileContents = file_get_contents( $path ); + + if( $patch && $mtime != $serverMTime ) { + + $response["status"] = "error"; + $response["message"] = "Client is out of sync"; + //DEBUG : file_put_contents($this->path.".conflict", "SERVER MTIME :".$serverMTime.", CLIENT MTIME :".$this->mtime); + return $response; + } elseif( strlen( trim( $patch ) ) == 0 && ! $content ) { + + // Do nothing if the patch is empty and there is no content + $response["status"] = "success"; + $response["data"] = array( + "mtime" => $serverMTime + ); + return $response; + } + + if( $file = fopen( $path, 'w' ) ) { + + if( $patch ) { + + $dmp = new diff_match_patch(); + $p = $dmp->patch_apply( $dmp->patch_fromText( $patch ), $fileContents ); + $content = $p[0]; + //DEBUG : file_put_contents($this->path.".orig",$fileContents ); + //DEBUG : file_put_contents($this->path.".patch", $this->patch); + } + + if( fwrite( $file, $content ) === false ) { + + $response["status"] = "error"; + $response["message"] = "could not write to file"; + } else { + + // Unless stat cache is cleared the pre-cached mtime will be + // returned instead of new modification time after editing + // the file. + clearstatcache(); + $response["status"] = "success"; + $response["data"] = array( + "mtime" => filemtime( $path ) + ); + } + fclose( $file ); + } else { + + $response["status"] = "error"; + $response["message"] = "Cannot Write to File"; + } + + } else { + + $response["status"] = "error"; + $response["message"] = "Not A File"; + } + return $response; + } + + public function move( $path, $new_path ) { + + $response = array( + "status" => "none", + ); + $path = self::formatPath( $path ); + $new_path = self::formatPath( $new_path ); + + if ( ! file_exists( $new_path ) ) { + + if( rename( $path, $new_path ) ) { + + $response["status"] = "success"; + } else { + + $response["status"] = "error"; + $response["message"] = "Could Not Rename"; + } + } else { + + $response["status"] = "error"; + $response["message"] = "Path Already Exists"; + } + return $response; } ////////////////////////////////////////////////////////////////// // OPEN (Returns the contents of a file) ////////////////////////////////////////////////////////////////// - public function open() { + public function open( $path ) { - if ( is_file( $this->path ) ) { + $response = array( + "status" => "none", + "message" => null, + ); + + $relative_path = self::cleanPath( $path ); + $path = self::formatPath( $path ); + + if ( is_file( $path ) ) { + + $output = file_get_contents( $path ); - $output = file_get_contents($this->path); - if ( extension_loaded( 'mbstring' ) ) { if ( ! mb_check_encoding( $output, 'UTF-8' ) ) { @@ -367,423 +588,372 @@ class Filemanager extends Common { } } - $this->status = "success"; - $this->data = '"content":' . json_encode( $output ); - $mtime = filemtime( $this->path ); - $this->data .= ', "mtime":'.$mtime; + $response["status"] = "success"; + $response["data"] = array( + "content" => $output, + "mtime" => filemtime( $path ), + "read_only" => ( ! Permissions::has_write( $path ) ), + ); } else { - $this->status = "error"; - $this->message = "Not A File :" . $this->path; + $response["status"] = "error"; + $response["message"] = "Error, {$path} is not a file."; } - - $this->respond(); + return $response; } + + ////////////////////////////////////////////////////////////////// + // OPEN IN BROWSER (Return URL) + ////////////////////////////////////////////////////////////////// + + public function preview( $path ) { - ////////////////////////////////////////////////////////////////// - // OPEN IN BROWSER (Return URL) - ////////////////////////////////////////////////////////////////// - - public function openinbrowser() { - $protocol = ( ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] != 'off' ) || $_SERVER['SERVER_PORT'] == 443 ) ? "https://" : "http://"; $domainName = $_SERVER['HTTP_HOST']; - $url = $protocol . WSURL . '/' . $this->rel_path; - $this->status = "success"; - $this->data = '"url":' . json_encode( rtrim( $url, "/" ) ); - $this->respond(); + $url = $protocol . WSURL . '/' . self::cleanPath( $path ); + $response = array( + "status" => "success", + "data" => rtrim( $url, "/" ), + ); + return $response; } - ////////////////////////////////////////////////////////////////// - // CREATE (Creates a new file or directory) - ////////////////////////////////////////////////////////////////// - - public function create() { - - // Create file - if ( $this->type == "file" ) { - - if ( ! file_exists( $this->path ) ) { - - if ( $file = fopen( $this->path, 'w' ) ) { - - // Write content - if ( $this->content ) { - - fwrite( $file, $this->content ); - } - $this->data = '"mtime":' . filemtime( $this->path ); - fclose( $file ); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Cannot Create File"; - } - } else { - - $this->status = "error"; - $this->message = "File Already Exists"; - } - } + static function recursive_copy( $source, $destination ) { - // Create directory - if ( $this->type == "directory" ) { - - if ( ! is_dir( $this->path ) ) { - - mkdir( $this->path ); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Directory Already Exists"; - } - } + $dir = opendir( $source ); + @mkdir( $source ); - $this->respond(); - } - - ////////////////////////////////////////////////////////////////// - // DELETE (Deletes a file or directory (+contents or only contents)) - ////////////////////////////////////////////////////////////////// - - public function delete( $keep_parent = false ) { - - if( Common::checkPath( $path ) ) { + if( is_dir( $source ) ) { + + @mkdir( $destination ); + } else { - $this->status = "error"; - $this->message = "No access."; - $this->respond(); return; } - function rrmdir( $path, $follow, $keep_parent = false ) { + while ( false !== ( $file = readdir( $dir ) ) ) { - if ( is_file( $path ) ) { - - unlink( $path ); - } else { - - $files = array_diff( scandir( $path ), array( '.', '..' ) ); - foreach ( $files as $file ) { - - if ( is_link( $path . "/" . $file ) ) { - - if ( $follow ) { - - rrmdir( $path . "/" . $file, $follow, false); - } - unlink( $path . "/" . $file ); - } elseif ( is_dir( $path . "/" . $file ) ) { - - rrmdir( $path . "/" . $file, $follow, false ); - } else { - - unlink( $path . "/" . $file ); - } - } - if( $keep_parent === false ) { - - rmdir( $path ); - return; - } else { + if ( ( $file != '.' ) && ( $file != '..' ) ) { - return; + if ( is_dir( $source . '/' . $file ) ) { + + self::recursive_copy( $source . '/' . $file, $destination . '/' . $file ); + } else { + + copy( $source . '/' . $file, $destination . '/' . $file ); } } } - - if ( file_exists( $this->path ) ) { - - if ( isset( $_GET['follow'] ) ) { - - rrmdir( $this->path, true, $keep_parent ); - } else { - - rrmdir( $this->path, false, $keep_parent ); - } - - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Path Does Not Exist "; - } - $this->respond(); + closedir( $dir ); } - - ////////////////////////////////////////////////////////////////// - // MODIFY (Modifies a file name/contents or directory name) - ////////////////////////////////////////////////////////////////// - - public function modify() { + static function recursive_delete( $path, $follow, $keep_parent = false ) { - // Change name - if ( $this->new_name ) { + $response = array( + "status" => "none", + "message" => null, + "keep_parent" => $keep_parent, + ); + $status = "none"; + + if ( is_file( $path ) ) { - $explode = explode( '/', $this->path ); - array_pop( $explode ); - $new_path = implode( "/", $explode ) . "/" . $this->new_name; - $new_path = $this->cleanPath( $new_path ); - - if ( ! file_exists( $new_path ) ) { - - if ( rename( $this->path, $new_path ) ) { - - //unlink($this->path); - $this->status = "success"; - } else { - - $this->status = "error"; - $this->message = "Could Not Rename"; - } - } else { - - $this->status = "error"; - $this->message = "Path Already Exists"; - } + unlink( $path ); } else { + + $files = array_diff( scandir( $path ), array( '.', '..' ) ); + foreach ( $files as $file ) { - // Change content - if ( $this->content || $this->patch ) { + if( is_link( $path . "/" . $file ) ) { - if ( $this->content == ' ' ) { - - $this->content = ''; // Blank out file - } - if ( $this->patch && ! $this->mtime ) { - - $this->status = "error"; - $this->message = "mtime parameter not found"; - $this->respond(); - return; - } - if ( is_file( $this->path ) ) { - - $serverMTime = filemtime( $this->path ); - $fileContents = file_get_contents( $this->path ); - - if ( $this->patch && $this->mtime != $serverMTime ) { + if( $follow ) { - $this->status = "error"; - $this->message = "Client is out of sync"; - //DEBUG : file_put_contents($this->path.".conflict", "SERVER MTIME :".$serverMTime.", CLIENT MTIME :".$this->mtime); - $this->respond(); - return; - } elseif ( strlen( trim( $this->patch ) ) == 0 && ! $this->content ) { - - // Do nothing if the patch is empty and there is no content - $this->status = "success"; - $this->data = '"mtime":' . $serverMTime; - $this->respond(); - return; - } - - if ( $file = fopen( $this->path, 'w' ) ) { - - if ( $this->patch ) { - - $dmp = new diff_match_patch(); - $p = $dmp->patch_apply( $dmp->patch_fromText( $this->patch ), $fileContents ); - $this->content = $p[0]; - //DEBUG : file_put_contents($this->path.".orig",$fileContents ); - //DEBUG : file_put_contents($this->path.".patch", $this->patch); - } - - if ( fwrite( $file, $this->content ) === false ) { - - $this->status = "error"; - $this->message = "could not write to file"; - } else { - - // Unless stat cache is cleared the pre-cached mtime will be - // returned instead of new modification time after editing - // the file. - clearstatcache(); - $this->data = '"mtime":'.filemtime( $this->path ); - $this->status = "success"; - } - - fclose( $file ); - } else { - - $this->status = "error"; - $this->message = "Cannot Write to File"; + $status = self::recursive_delete( $path . "/" . $file, $follow, false ); } + $status = unlink( $path . "/" . $file ); + } elseif( is_dir( $path . "/" . $file ) ) { + $status = self::recursive_delete( $path . "/" . $file, $follow, false ); } else { - $this->status = "error"; - $this->message = "Not A File"; + $status = unlink( $path . "/" . $file ); } - } else { + } + + if( $keep_parent === false ) { - $file = fopen( $this->path, 'w' ); - fclose( $file ); - $this->data = '"mtime":' . filemtime ($this->path); - $this->status = "success"; + $status = rmdir( $path ); } } - $this->respond(); + $response["message"] = "Removed $path"; + $response["status"] = $status; + return $response; + } + + function reverse_recursive_delete( $start, $stop, $files ) { + + $response = array( + "status" => "none", + "message" => null, + ); + $path_array = explode( "/", $start ); + + do { + + $p = implode( "/", $path_array ); + + if( is_dir( $p ) && $p !== $stop ) { + + if( $files ) { + + $this->recursive_delete( $p, true ); + } else { + + $files = array_diff( scandir( $p ), array( '.', '..' ) ); + + if( count( $files ) == 0 ) { + + $this->recursive_delete( $p, true ); + } else { + + break; + } + } + } else { + + break; + } + array_pop( $path_array ); + } while( count( $path_array ) > 1 ); + + return $response; } ////////////////////////////////////////////////////////////////// - // DUPLICATE (Creates a duplicate of the object - (cut/copy/paste) + // SEARCH ////////////////////////////////////////////////////////////////// - public function duplicate() { - - if ( ! file_exists( $this->path ) ) { + public function search( $path, $query, $options ) { + + $response = array( + "status" => "none", + "message" => null, + ); + + if( ! common::isAbsPath( $path ) ) { - $this->status = "error"; - $this->message = "Invalid Source"; + $path = WORKSPACE . "/$path"; } - function recurse_copy( $src, $dst ) { + if ( ! function_exists( 'shell_exec' ) ) { - $dir = opendir( $src ); - @mkdir( $dst ); - while ( false !== ( $file = readdir( $dir ) ) ) { + $response["status"] = "error"; + $response["message"] = "Shell_exec() Command Not Enabled."; + } else { + + $return = array(); + $input = str_replace( '"', '', $query ); + $cmd = 'find -L ' . escapeshellarg( $path ) . ' -iregex ' . escapeshellarg( '.*' . $options["filetype"] ) . ' -type f -print0 | xargs -0 grep -i -I -n -R -H ' . escapeshellarg( $input ) . ''; + $output = shell_exec( $cmd ); + $output_arr = explode( "\n", $output ); + foreach ( $output_arr as $line ) { - if (( $file != '.' ) && ( $file != '..' )) { + $data = explode( ":", $line ); + $da = array(); + if ( count( $data ) > 2 ) { - if ( is_dir( $src . '/' . $file ) ) { - - recurse_copy( $src . '/' . $file, $dst . '/' . $file ); - } else { - - copy( $src . '/' . $file, $dst . '/' . $file ); - } + $da['line'] = $data[1]; + $da['file'] = str_replace( $path, '', $data[0] ); + $da['result'] = str_replace( WORKSPACE . '/', '', $data[0] ); + $da['string'] = str_replace( $data[0] . ":" . $data[1] . ':', '', $line ); + $return[] = $da; } } - closedir($dir); - } - - if ( $this->status != "error" ) { - - if ( is_file( $this->path ) ) { + if ( count( $return ) == 0 ) { - copy( $this->path, $this->destination ); - $this->status = "success"; + $response["status"] = "error"; + $response["message"] = "No Results Returned"; } else { - recurse_copy( $this->path, $this->destination ); - if ( ! $this->response ) { - - $this->status = "success"; - } + $response["status"] = "success"; + $response["data"] = array(); + $response["data"]["index"] = $return; + $response["data"]["cmd"] = $cmd; + $response["data"]["output"] = $output; + $response["data"]["output_array"] = $output_arr; } } - $this->respond(); + 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() { - - // Check that the path is a directory - if ( is_file( $this->path ) ) { + public function upload_blob( $path, $index, $blob ) { + + $response = array( + "status" => "none", + "message" => "", + ); + + if( ! Permissions::has_write( $path ) ) { - $this->status = "error"; - $this->message = "Path Not A Directory"; + $response["status"] = "error"; + $response["message"] = "You do not have access to write to this file."; + return $response; + } + + if( ! common::isAbsPath( $path ) ) { + + $path = WORKSPACE . "/$path"; + } + + if( ! is_dir( UPLOAD_CACHE . "$path/" ) ) { + + mkdir( UPLOAD_CACHE . "$path/", 0755, true ); + } + + $handle = fopen( UPLOAD_CACHE . "$path/$index", "a" ); + $status = fwrite( $handle, $blob ); + fclose( $handle ); + + if( $status === false ) { + + $response["status"] = "error"; + $response["message"] = "File could not be written to."; } else { - // Handle upload - $info = array(); - foreach( $_FILES['upload']['name'] as $key => $value ) { - - if ( ! empty( $value ) ) { - - $filename = $value; - $add = $this->path."/$filename"; - if ( @move_uploaded_file( $_FILES['upload']['tmp_name'][$key], $add ) ) { - - $info[] = array( - "name"=>$value, - "size"=>filesize($add), - "url"=>$add, - "thumbnail_url"=>$add, - "delete_url"=>$add, - "delete_type"=>'DELETE' - ); - } - } - } - $this->upload_json = json_encode( $info ); + $response["status"] = "success"; + $response["path"] = $path; + $response["bytes"] = $status; + $response["message"] = "$status bytes written to file."; } - $this->respond(); + return $response; } - ////////////////////////////////////////////////////////////////// - // RESPOND (Outputs data in JSON [JSEND] format) - ////////////////////////////////////////////////////////////////// - - public function respond() { - - // Success /////////////////////////////////////////////// - if ( $this->status=="success" ) { - - if ( $this->data ) { - - $json = '{"status":"success","data":{' . $this->data . '}}'; - } else { - $json = '{"status":"success","data":null}'; - } + public function upload_stitch( $path ) { + + $response = array( + "status" => "none", + "message" => "", + ); + $status = true; + + if( ! Permissions::has_write( $path ) ) { - // Upload JSON /////////////////////////////////////////// - } elseif ( $this->upload_json != '' ) { - - $json = $this->upload_json; - // Error ///////////////////////////////////////////////// - } else { - - $json = '{"status":"error","message":"'.$this->message.'"}'; + $response["status"] = "error"; + $response["message"] = "You do not have access to write to this file."; + return $response; } - // Output //////////////////////////////////////////////// - echo( $json ); + if( ! common::isAbsPath( $path ) ) { + + $path = WORKSPACE . "/$path"; + } + + $cache_path = UPLOAD_CACHE . "$path/"; + $dir = dirname( $path ); + $name = basename( $path ); + + if( ! is_dir( $dir ) ) { + + mkdir( $dir, 0755, true ); + } + + $files = scandir( $cache_path ); + + 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 ); + } + } + + $rm_status = $this->reverse_recursive_delete( $cache_path, UPLOAD_CACHE, false ); + + if( $status === false ) { + + $response["status"] = "error"; + $response["message"] = "File could not be written to."; + } else { + + $response["status"] = "success"; + $response["path"] = $path; + $response["bytes"] = $status; + $response["message"] = "$status bytes written to file."; + $response["remove"] = $rm_status; + } + return $response; } - ////////////////////////////////////////////////////////////////// - // Clean a path - ////////////////////////////////////////////////////////////////// - - public static function cleanPath( $path ) { + public function upload_clean_stitches() { - // Prevent going out of the workspace - while ( strpos( $path, '../' ) !== false ) { - - $path = str_replace( '../', '', $path ); - } + $path = UPLOAD_CACHE; - if( Filemanager::isAbsPath( $path ) ) { - - $full_path = $path; - } else { - - $full_path = WORKSPACE . "/" . $path; - } - - /** - * If a file with an invalid character exists and the user is - * trying to rename or delete it, allow the actual file name. - */ - - $invalid_characters = preg_match( '/[^A-Za-z0-9\-\._@\/\ ]/', $path ); - - if( $invalid_characters && ! ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - - exit( '{"status":"error","message":"Error, the filename contains invalid characters, please either rename or delete it."}' ); - } elseif( $invalid_characters && ( $_GET['action'] == "modify" || $_GET['action'] == "delete" ) ) { - } else { - - $path = preg_replace( '/[^A-Za-z0-9\-\._\/\ ]/', '', $path ); - } - return $path; } } diff --git a/components/filemanager/context_menu.json b/components/filemanager/context_menu.json index 299acb7..1c52147 100755 --- a/components/filemanager/context_menu.json +++ b/components/filemanager/context_menu.json @@ -1,182 +1,194 @@ [ - { - "title": "New File", - "icon": "icon-doc-text", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.createNode($('#context-menu').attr('data-path'),'file');" - }, - { - "title": "New Folder", - "icon": "icon-folder", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.createNode($('#context-menu').attr('data-path'),'directory');" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "directory-only", - "onclick": null - }, - { - "title": "Search", - "icon": "icon-target", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.search($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "directory-only", - "onclick": null - }, - { - "title": "Upload Files", - "icon": "icon-upload", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.uploadToNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Preview", - "icon": "icon-eye", - "applies-to" : "both no-external", - "onclick": "codiad.filemanager.openInBrowser($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "file-only no-external", - "onclick": null - }, - { - "title": "Break", - "icon": null, - "applies-to" : "directory-only", - "onclick": null - }, - { - "title": "Copy", - "icon": "icon-doc", - "applies-to" : "both", - "onclick": "codiad.filemanager.copyNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Paste", - "icon": "icon-docs", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.pasteNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "non-root", - "onclick": null - }, - { - "title": "Rename", - "icon": "icon-pencil", - "applies-to" : "non-root", - "onclick": "codiad.filemanager.renameNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Rename Project", - "icon": "icon-pencil", - "applies-to" : "root-only", - "onclick": "codiad.project.rename($('#context-menu').attr('data-path'),$('#context-menu').attr('data-name'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "non-root", - "onclick": null - }, - { - "title": "Delete", - "icon": "icon-cancel-circled", - "applies-to" : "non-root", - "onclick": "codiad.filemanager.deleteNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "both no-external", - "onclick": null - }, - { - "title": "Delete Contents", - "icon": "icon-cancel-circled", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.deleteInnerNode($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "directory-only", - "onclick": null - }, - { - "title": "Download", - "icon": "icon-download", - "applies-to" : "both no-external", - "onclick": "codiad.filemanager.download($('#context-menu').attr('data-path'));" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "directory-only", - "onclick": null - }, - { - "title": "Rescan", - "icon": "icon-arrows-ccw", - "applies-to" : "directory-only", - "onclick": "codiad.filemanager.rescan($('#context-menu').attr('data-path'));" - }, - { - "title": "Copy", - "icon": "icon-doc", - "applies-to" : "editor-only", - "onclick": "document.execCommand( 'copy' );" - }, - { - "title": "Cut", - "icon": "icon-pencil", - "applies-to" : "editor-only", - "onclick": "document.execCommand( 'cut' );" - }, - { - "title": "Paste", - "icon": "icon-docs", - "applies-to" : "editor-only", - "onclick": "codiad.editor.paste();" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "editor-only", - "onclick": null - }, - { - "title": "Find", - "icon": "icon-search", - "applies-to" : "editor-only", - "onclick": "codiad.editor.openSearch('find');" - }, - { - "title": "Replace", - "icon": "icon-pencil", - "applies-to" : "editor-only", - "onclick": "codiad.editor.openSearch('replace');" - }, - { - "title": "Break", - "icon": null, - "applies-to" : "editor-only", - "onclick": null - }, - { - "title": "Sort", - "icon": "icon-box", - "applies-to" : "editor-only", - "onclick": "codiad.editor.openSort();" - } + { + "title": "New File", + "icon": "icon-doc-text", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.create_node($('#context-menu').attr('data-path'),'file');" + }, + { + "title": "New Folder", + "icon": "icon-folder", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.create_node($('#context-menu').attr('data-path'),'directory');" + }, + { + "title": "Break", + "icon": null, + "applies-to": "directory-only", + "onclick": null + }, + { + "title": "Search", + "icon": "icon-target", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.search($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "directory-only", + "onclick": null + }, + { + "title": "Upload Files", + "icon": "icon-upload", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.uploadToNode($('#context-menu').attr('data-path'));" + }, + { + "title": "Preview", + "icon": "icon-eye", + "applies-to": "both no-external", + "onclick": "codiad.filemanager.preview($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "file-only no-external", + "onclick": null + }, + { + "title": "Archive", + "icon": "icon-archive", + "applies-to": "directory-only non-root", + "onclick": "codiad.filemanager.archive( $('#context-menu').attr('data-path') );" + }, + { + "title": "Unarchive", + "icon": "icon-archive", + "applies-to": "file-only non-root", + "onclick": "codiad.filemanager.unarchive( $('#context-menu').attr('data-path') );" + }, + { + "title": "Break", + "icon": null, + "applies-to": "directory-only", + "onclick": null + }, + { + "title": "Copy", + "icon": "icon-doc", + "applies-to": "both", + "onclick": "codiad.filemanager.copyNode($('#context-menu').attr('data-path'));" + }, + { + "title": "Paste", + "icon": "icon-docs", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.paste_node($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "non-root", + "onclick": null + }, + { + "title": "Rename", + "icon": "icon-pencil", + "applies-to": "non-root", + "onclick": "codiad.filemanager.open_rename($('#context-menu').attr('data-path'));" + }, + { + "title": "Rename Project", + "icon": "icon-pencil", + "applies-to": "root-only", + "onclick": "codiad.project.rename($('#context-menu').attr('data-path'),$('#context-menu').attr('data-name'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "non-root", + "onclick": null + }, + { + "title": "Delete", + "icon": "icon-cancel-circled", + "applies-to": "non-root", + "onclick": "codiad.filemanager.deleteNode($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "both no-external", + "onclick": null + }, + { + "title": "Delete Contents", + "icon": "icon-cancel-circled", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.delete_children_nodes($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "directory-only", + "onclick": null + }, + { + "title": "Download", + "icon": "icon-download", + "applies-to": "both no-external", + "onclick": "codiad.filemanager.download($('#context-menu').attr('data-path'));" + }, + { + "title": "Break", + "icon": null, + "applies-to": "directory-only", + "onclick": null + }, + { + "title": "Rescan", + "icon": "icon-arrows-ccw", + "applies-to": "directory-only", + "onclick": "codiad.filemanager.rescan($('#context-menu').attr('data-path'));" + }, + { + "title": "Copy", + "icon": "icon-doc", + "applies-to": "editor-only", + "onclick": "document.execCommand( 'copy' );" + }, + { + "title": "Cut", + "icon": "icon-pencil", + "applies-to": "editor-only", + "onclick": "document.execCommand( 'cut' );" + }, + { + "title": "Paste", + "icon": "icon-docs", + "applies-to": "editor-only", + "onclick": "codiad.editor.paste();" + }, + { + "title": "Break", + "icon": null, + "applies-to": "editor-only", + "onclick": null + }, + { + "title": "Find", + "icon": "icon-search", + "applies-to": "editor-only", + "onclick": "codiad.editor.openSearch('find');" + }, + { + "title": "Replace", + "icon": "icon-pencil", + "applies-to": "editor-only", + "onclick": "codiad.editor.openSearch('replace');" + }, + { + "title": "Break", + "icon": null, + "applies-to": "editor-only", + "onclick": null + }, + { + "title": "Sort", + "icon": "icon-box", + "applies-to": "editor-only", + "onclick": "codiad.editor.openSort();" + } ] \ No newline at end of file diff --git a/components/filemanager/controller.php b/components/filemanager/controller.php index 4822eb3..8d082cd 100755 --- a/components/filemanager/controller.php +++ b/components/filemanager/controller.php @@ -1,95 +1,354 @@ "none", +); + +if( ! empty($_GET['action'] ) ) { + + $action = $_GET['action']; } else { - exit('{"status":"error","data":{"error":"No Action Specified"}}'); + + $response["status"] = "error"; + $response["data"] = array( + "error" => "No action specified" + ); + exit( json_encode( $response ) ); } - ////////////////////////////////////////////////////////////////// - // Ensure Project Has Been Loaded - ////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// Ensure Project Has Been Loaded +////////////////////////////////////////////////////////////////// -if (!isset($_SESSION['project'])) { - $_GET['action']='get_current'; - $_GET['no_return']='true'; - require_once('../project/controller.php'); -} - - ////////////////////////////////////////////////////////////////// - // Security Check - ////////////////////////////////////////////////////////////////// - -if (!checkPath($_GET['path'])) { - die('{"status":"error","message":"Invalid Path"}'); +if( ! isset( $_SESSION['project'] ) ) { + + $_GET['action'] = 'get_current'; + $_GET['no_return'] = 'true'; + require_once('../project/controller.php'); } - ////////////////////////////////////////////////////////////////// - // Define Root - ////////////////////////////////////////////////////////////////// - - $_GET['root'] = WORKSPACE; - - ////////////////////////////////////////////////////////////////// - // Handle Action - ////////////////////////////////////////////////////////////////// - - $Filemanager = new Filemanager($_GET, $_POST, $_FILES); - $Filemanager->project = @$_SESSION['project']['path']; - -switch ($action) { - case 'index': - $Filemanager->index(); - break; - case 'search': - $Filemanager->search(); - break; - case 'find': - $Filemanager->find(); - break; - case 'open': - $Filemanager->open(); - break; - case 'open_in_browser': - $Filemanager->openinbrowser(); - break; - case 'create': - $Filemanager->create(); - break; - case 'delete': - $Filemanager->delete(); - break; - case 'deleteInner': - $Filemanager->delete( true ); - break; - case 'modify': - $Filemanager->modify(); - break; - case 'duplicate': - $Filemanager->duplicate(); - break; - case 'upload': - $Filemanager->upload(); - break; - default: - exit('{"status":"fail","data":{"error":"Unknown Action"}}'); +if( isset( $_GET["path"] ) || isset( $_POST["path"] ) ) { + + $path = isset( $_GET["path"] ) ? $_GET["path"] : $_POST["path"]; +} else { + + $response["status"] = "error"; + $response["message"] = "Missing path."; + $response["GET"] = $_GET; + $response["POST"] = $_POST; + exit( json_encode( $response ) ); } + +////////////////////////////////////////////////////////////////// +// Security Check +////////////////////////////////////////////////////////////////// + +$access = Permissions::get_access( $path ); + +if ( ! Permissions::check_access( "read", $access ) ) { + + $response["status"] = "error"; + $response["message"] = "Invalid access to path"; + $response["path"] = $path; + exit( json_encode( $response ) ); +} + +if( isset( $_GET["destination"] ) || isset( $_POST["destination"] ) ) { + + $destination = isset( $_GET["destination"] ) ? $_GET["destination"] : $_POST["destination"]; + + if ( ! checkPath( $destination ) ) { + + $response["status"] = "error"; + $response["message"] = "Invalid destination"; + exit( json_encode( $response ) ); + } +} + +////////////////////////////////////////////////////////////////// +// Handle Action +////////////////////////////////////////////////////////////////// + +$Filemanager = new Filemanager(); + +switch( $action ) { + + case 'archive': + + if( ! isset( $path ) ) { + + exit( formatJSEND( "error", "No path specified." ) ); + } + + if( ! Permissions::check_access( "create", $access ) ) { + + exit( formatJSEND( "error", "Invalid access to create archive." ) ); + } + + $Archive = new Archive(); + $path = $Filemanager->formatPath( $path ); + $result = $Archive->compress( $path ); + + if( $result ) { + + $response = formatJSEND( "success", null ); + } else { + + $response = formatJSEND( "error", "Could not create archive." ); + } + + exit( $response ); + break; + + case( 'copy' ): + + if( isset( $_POST["replace"] ) ) { + + $replace = $_POST["replace"]; + } else { + + $replace = false; + } + + $response = $Filemanager->copy( $path, $destination, $replace ); + break; + + case 'create': + + if( isset( $_GET["type"] ) ) { + + $type = $_GET["type"]; + $response = $Filemanager->create( $path, $type ); + } else { + + $response["status"] = "error"; + $response["message"] = "No filetype set"; + } + break; + + case 'delete': + + $response = $Filemanager->delete( $path, true ); + break; + + case 'delete_children': + + $response = $Filemanager->delete( $path, true, true ); + break; + + case 'find': + + if( ! isset( $_GET["query"] ) ) { + + $response["status"] = "error"; + $response["message"] = "Missing search query."; + } else { + + $query = $_GET["query"]; + if( isset( $_GET["options"] ) ) { + + $options = $_GET["options"]; + } + + $response = $Filemanager->find( $path, $query, @$options ); + } + break; + + case 'index': + + $response = $Filemanager->index( $path ); + break; + + case 'modify': + + if( isset( $_POST["data"] ) ) { + + $data = json_decode( $_POST["data"], true ); + + if( json_last_error() !== JSON_ERROR_NONE ) { + + $data = json_decode( stripslashes( $_POST["data"] ), true ); + } + + if( json_last_error() !== JSON_ERROR_NONE ) { + + $data = array(); + } + + if( isset( $data["content"] ) || isset( $data["patch"] ) ) { + + $content = isset( $data["content"] ) ? $data["content"] : ""; + $patch = isset( $data["patch"] ) ? $data["patch"] : false; + $mtime = isset( $data["mtime"] ) ? $data["mtime"] : 0; + + $response = $Filemanager->modify( $path, $content, $patch, $mtime ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing modification content"; + } + } else { + + $response["status"] = "error"; + $response["message"] = "Missing save data"; + } + break; + + case 'move': + + if( isset( $destination ) ) { + + $response = $Filemanager->move( $path, $destination ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing destination"; + } + break; + + case 'open': + + $response = $Filemanager->open( $path ); + break; + + case 'preview': + + $response = $Filemanager->preview( $path ); + break; + + case 'rename': + + if( isset( $destination ) ) { + + $response = $Filemanager->move( $path, $destination ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing destination"; + } + break; + + case 'search': + + if( isset( $path ) && isset( $_POST["query"] ) ) { + + $query = $_POST["query"]; + + if( isset( $_POST["options"] ) ) { + + $options = json_decode( $_POST["options"], true ); + } else { + + $options = array(); + } + + $response = $Filemanager->search( $path, $query, $options ); + } else { + + $response["status"] = "error"; + $response["message"] = "Missing search query."; + } + break; + + case( "stitch" ): + + $response = $Filemanager->stitch( $path ); + break; + + case 'unarchive': + + if( ! isset( $path ) ) { + + exit( formatJSEND( "error", "No path specified." ) ); + } + + if( ! Permissions::check_access( "create", $access ) ) { + + exit( formatJSEND( "error", "Invalid access to unzip archive." ) ); + } + + $Archive = new Archive(); + $path = $Filemanager->formatPath( $path ); + $result = $Archive->decompress( $path ); + + if( $result && $result["status"] == "success" ) { + + $response = formatJSEND( "success", $result ); + } else { + + $response = formatJSEND( "error", $result["message"] ); + } + + exit( $response ); + break; + + case 'upload_blob': + + if( ! isset( $_POST["data"] ) ) { + + $response["status"] = "error"; + $response["data"] = array( + "error" => "No blob given" + ); + exit( json_encode( $response ) ); + } + + if( ! isset( $_POST["path"] ) ) { + + $response["status"] = "error"; + $response["data"] = array( + "error" => "No path given" + ); + exit( json_encode( $response ) ); + } + + if( ! isset( $_POST["index"] ) ) { + + $response["status"] = "error"; + $response["data"] = array( + "error" => "No index given" + ); + exit( json_encode( $response ) ); + } + + $blob = @file_get_contents( $_POST["data"] ); + $path = $_POST["path"]; + $index = $_POST["index"]; + + $response = $Filemanager->upload_blob( $path, $index, $blob ); + break; + + case 'upload_stitch': + + $Filemanager->upload_stitch( $path ); + break; + + default: + + $response["status"] = "error"; + $response["data"] = array( + "error" => "Unknown action" + ); + break; +} + +exit( json_encode( $response ) ); diff --git a/components/filemanager/dialog.php b/components/filemanager/dialog.php index 030a0ee..4953bf5 100755 --- a/components/filemanager/dialog.php +++ b/components/filemanager/dialog.php @@ -14,147 +14,186 @@ require_once('class.filemanager.php'); checkSession(); -?> -
- - - - - - - - - - - - - - - - - -
- - - - - -
- - - - -


">

- - - -


- -

- - - - -
- - - - - - - - - - - - - - -
- - -    - - -
- - "> -
-

-    
- - - + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + +
+

Error, unknown file type.

+
+ + + + +
+			
+		
+ + + + + + + + + + + + + + +
+ + +    + + +
+ + "> +
+

+		
+ + + +
+ +
Loading File Selector ...
+
+ +
+ +
Loading Uploader ...
+
+ -
+?> diff --git a/components/filemanager/dialog_upload.php b/components/filemanager/dialog_upload.php deleted file mode 100755 index 80d9831..0000000 --- a/components/filemanager/dialog_upload.php +++ /dev/null @@ -1,58 +0,0 @@ - - -
- - - - -
-
-
- - diff --git a/components/filemanager/download.php b/components/filemanager/download.php index bba0cf5..0329c3b 100755 --- a/components/filemanager/download.php +++ b/components/filemanager/download.php @@ -1,84 +1,109 @@ \|]#i', $_GET['path']) //illegal chars in filenames - || substr_count($_GET['path'], './') > 0) { // change directory up to escape Workspace - exit(''); +$response = array( + "status" => "none", + "message" => null, +); + +if( ! isset( $_GET["path"] ) && ! isset( $_POST["path"] ) ) { + + $response["status"] = "error"; + $response["message"] = "Missing path."; + exit( json_encode( $response ) ); } - ////////////////////////////////////////////////////////////////// - // Run Download - ////////////////////////////////////////////////////////////////// +$path = ( isset( $_GET["path"] ) ) ? $_GET["path"] : $_POST["path"]; +$full_path = ""; -if (isset($_GET['type']) && ($_GET['type']=='directory' || $_GET['type']=='root')) { - // Create tarball - $filename = explode("/", $_GET['path']); - //$filename = array_pop($filename) . "-" . date('Y.m.d') . ".tar.gz"; - $filename = array_pop($filename) . "-" . date('Y.m.d'); - $targetPath = DATA . '/'; - $dir = WORKSPACE . '/' . $_GET['path']; - if (!is_dir($dir)) { - exit(''); - } - - ////////////////////////////////////////////////////////////////// - // Check system() command and a non windows OS - ////////////////////////////////////////////////////////////////// - if (extension_loaded('zip')) { //Check if zip-Extension is availiable - //build zipfile - require_once 'class.dirzip.php'; - - $filename .= '.zip'; - $download_file = $targetPath.$filename; - DirZip::zipDir($dir, $targetPath .$filename); - } elseif (isAvailable('system') && stripos(PHP_OS, 'win') === false) { - # Execute the tar command and save file - $filename .= '.tar.gz'; - - system("tar -pczf ".escapeshellarg($targetPath.$filename)." -C ".escapeshellarg(WORKSPACE)." ".escapeshellarg($_GET['path'])); - $download_file = $targetPath.$filename; - } else { - exit(''); - } +if( Common::isAbsPath( $path ) ) { + + $full_path = realpath( $path ); } else { - $filename = explode("/", $_GET['path']); - $filename = array_pop($filename); - $download_file = WORKSPACE . '/' . $_GET['path']; + + $full_path = WORKSPACE . "/$path"; + $full_path = realpath( $full_path ); } - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="'.basename($filename).'"'); - header('Content-Transfer-Encoding: binary'); - header('Expires: 0'); - header('Cache-Control: must-revalidate'); - header('Pragma: public'); - header('Content-Length: ' . filesize($download_file)); -if (ob_get_contents()) { - ob_end_clean(); +if( $full_path === false ) { + + $response["status"] = "error"; + $response["message"] = "Invalid path."; + exit( json_encode( $response ) ); } - flush(); - readfile($download_file); - // Remove temp tarball -if ($_GET['type']=='directory' || $_GET['type']=='root') { - unlink($download_file); + +if( ! Permissions::has_read( $path ) ) { + + $response["status"] = "error"; + $response["message"] = "You do not have access to this path."; + exit( json_encode( $response ) ); +} + +if( is_dir( $full_path ) ) { + + $temp_path = tempnam( sys_get_temp_dir(), 'codiad_download_' . date( "U" ) ); + $result = Archive::compress( $full_path, $temp_path, 'default' ); + $mime_type = mime_content_type( $temp_path ); + + if( in_array( $mime_type, array_keys( Archive::MIME_TYPE_EXTENSIONS ) ) ) { + + $extension = Archive::MIME_TYPE_EXTENSIONS["$mime_type"]; + } + + if( $result ) { + + header( 'Content-Description: File Transfer' ); + header( 'Content-Type: application/octet-stream' ); + header( 'Content-Disposition: attachment; filename="' . basename( $full_path ) . ".$extension" . '"' ); + header( 'Content-Transfer-Encoding: binary' ); + header( 'Expires: 0' ); + header( 'Cache-Control: must-revalidate' ); + header( 'Pragma: public' ); + header( 'Content-Length: ' . filesize( $temp_path ) ); + + if( ob_get_contents() ) { + + ob_end_clean(); + } + + flush(); + file_put_contents( "php://output", file_get_contents( $temp_path ) ); + } else { + + $response["status"] = "error"; + $response["message"] = "An archive could not be created."; + } + unlink( $temp_path ); +} else { + + header( 'Content-Description: File Transfer' ); + header( 'Content-Type: application/octet-stream' ); + header( 'Content-Disposition: attachment; filename="' . basename( $full_path ) . '"' ); + header( 'Content-Transfer-Encoding: binary' ); + header( 'Expires: 0' ); + header( 'Cache-Control: must-revalidate' ); + header( 'Pragma: public' ); + header( 'Content-Length: ' . filesize( $full_path ) ); + + if( ob_get_contents() ) { + + ob_end_clean(); + } + + flush(); + readfile( $full_path ); } diff --git a/components/filemanager/init.js b/components/filemanager/init.js index ef867a6..1260b48 100755 --- a/components/filemanager/init.js +++ b/components/filemanager/init.js @@ -5,7 +5,7 @@ */ ( function( global, $ ) { - var codiad = global.codiad; + let codiad = global.codiad; $( window ).load( function() { @@ -18,56 +18,118 @@ clipboard: '', controller: 'components/filemanager/controller.php', dialog: 'components/filemanager/dialog.php', - dialogUpload: 'components/filemanager/dialog_upload.php', - preview: null, - refresh_interval: null, - - init: async function() { + file_preview_list: { - this.noAudio = [ - //Audio + audio: [ 'aac', 'aif', 'mp3', 'mp4', - 'wav', 'ogg', - ]; - this.noFiles = [ - //Files + 'wav', + ], + files: [ 'exe', 'pdf', - 'zip', + 'rar', 'tar', 'tar.gz', - ]; - this.noImages = [ - //Images + 'zip', + ], + image: [ + 'bmp', + 'gif', 'ico', 'icon', - 'jpg', 'jpeg', + 'jpg', 'png', - 'gif', - 'bmp', - ]; + ], + }, + file_reader: null, + files: [], + node: { - - this.noOpen = this.noAudio.concat( this.noFiles, this.noImages ), - this.noBrowser = this.noAudio.concat( this.noImages ), + accept: function( e, i ) { - // Initialize node listener - this.nodeListener(); - this.auto_reload = ( await codiad.settings.get_option( "codiad.filemanager.autoReloadPreview" ) == "true" ); + return true; + }, - console.log( this.auto_reload ); + drag: function( e, i ) { + + }, + drop: function( e, i ) { + + let _this = codiad.filemanager; + let drag = i.helper[0]; + let drop = e.target; + + $( drop ).removeClass( "drag_over" ); + + if( ! $( drop ).attr( "data-path" ) ) { + + drop = $( drop ).children( 'a' ); + } + + console.log( drop ); + console.log( drag ); + + let drop_path = $( drop ).attr( "data-path" ); + let drag_path = $( drag ).children( "a" ).attr( "data-path" ); + let path = drag_path; + let newPath = `${drop_path}/` + path.split( "/" ).pop(); + + console.log( path, newPath ); + _this.rename_node( path, newPath ); + }, + + out: function( e, i ) { + + let drag = i.helper[0]; + let drop = e.target; + + $( drop ).removeClass( "drag_over" ); + }, + + over: function( e, i ) { + + let drag = i.helper[0]; + let drop = e.target; + + $( drop ).addClass( "drag_over" ); + }, + + start: function( e, i ) { + + let drag = i.helper[0]; + $( drag ).addClass( "drag_start" ); + $( drag ).children( 'a' ).removeClass( "a:hover" ); + }, + + stop: function( e, i ) { + + let drag = i.helper[0]; + $( drag ).removeClass( "drag_start" ); + //$( drag ).removeClass( "hover" ); + }, + }, + opened_folders: [], + post_max_size: ( 1024*1024 ), + preview_window: null, + refresh_interval: null, + selected: [], + + init: async function() { + + let _this = codiad.filemanager; + + /* Reload the page when saving auto reload preview */ amplify.subscribe( 'settings.save', async function() { let option = ( await codiad.settings.get_option( "codiad.filemanager.autoReloadPreview" ) == "true" ); if( option != codiad.filemanager.auto_reload ) { - //codiad.auto_save.reload_interval(); window.location.reload( true ); } }); @@ -75,143 +137,451 @@ /* Subscribe to know when a file become active. */ amplify.subscribe( 'active.onFocus', async function( path ) { - let _this = codiad.filemanager; let editor = codiad.editor.getActive(); if( _this.auto_reload && editor !== null ) { - codiad.editor.getActive().addEventListener( "change", _this.refreshPreview ); + codiad.editor.getActive().addEventListener( "change", _this.refresh_preview ); } }); - // Load uploader - $.loadScript( "components/filemanager/upload_scripts/jquery.ui.widget.js", true ); - $.loadScript( "components/filemanager/upload_scripts/jquery.iframe-transport.js", true ); - $.loadScript( "components/filemanager/upload_scripts/jquery.fileupload.js", true ); - }, - - ////////////////////////////////////////////////////////////////// - // Listen for dbclick events on nodes - ////////////////////////////////////////////////////////////////// - - nodeListener: function() { + /* + maybe we should have this calcualted as the file is being uploaded. + this may allow for a more dynamic upload speed for faster + connections and a more stable upload for slower connections. + */ + _this.calculate_upload_variables(); + _this.node_listeners(); - let _this = this; - - $( '#file-manager' ).on( 'selectstart', false ); - - $( '#file-manager span' ) - .live( 'click', function() { // Open or Expand - if( $( this ).parent().children( "a" ).attr( 'data-type' ) == 'directory' ) { - _this.index( $( this ).parent().children( "a" ) - .attr( 'data-path' ) ); - } else { - _this.openFile( $( this ).parent().children( "a" ) - .attr( 'data-path' ) ); - } - if( !$( this ).hasClass( 'none' ) ) { - if( $( this ).hasClass( 'plus' ) ) { - $( this ).removeClass( 'plus' ) - $( this ).addClass( 'minus' ); - } else { - $( this ).removeClass( 'minus' ) - $( this ).addClass( 'plus' ); - } + $( document ).on( 'dragenter', function( e ) { + + let d = e.originalEvent.dataTransfer; + + if( d && e.target.className !== "ace_scroller" ) { + + let files = d.files; + let items = d.items; + + console.log( 'dragenter', e, e.target, files, items, items[0].webkitGetAsEntry() ); + _this.upload_overlay_on(); } }); - $( '#file-manager a' ) - .live( 'dblclick', function() { // Open or Expand - if( !codiad.editor.settings.fileManagerTrigger ) { - if( $( this ) - .hasClass( 'directory' ) ) { - _this.index( $( this ) - .attr( 'data-path' ) ); - } else { - _this.openFile( $( this ) - .attr( 'data-path' ) ); - } - if( !$( this ).parent().children( "span" ).hasClass( 'none' ) ) { - if( $( this ).parent().children( "span" ).hasClass( 'plus' ) ) { - $( this ).parent().children( "span" ).removeClass( 'plus' ) - $( this ).parent().children( "span" ).addClass( 'minus' ); - } else { - $( this ).parent().children( "span" ).removeClass( 'minus' ) - $( this ).parent().children( "span" ).addClass( 'plus' ); - } - } - } + + $( '.drop-overlay' ).on( 'drag dragstart dragend dragover dragenter dragleave drop', function( e ) { + + //console.log( 'drag dragstart dragend dragover dragenter dragleave drop', e ); }) - .live( 'click', function() { // Open or Expand - if( codiad.editor.settings.fileManagerTrigger ) { - if( $( this ) - .hasClass( 'directory' ) ) { - _this.index( $( this ) - .attr( 'data-path' ) ); - } else { - _this.openFile( $( this ) - .attr( 'data-path' ) ); - } - if( !$( this ).parent().children( "span" ).hasClass( 'none' ) ) { - if( $( this ).parent().children( "span" ).hasClass( 'plus' ) ) { - $( this ).parent().children( "span" ).removeClass( 'plus' ) - $( this ).parent().children( "span" ).addClass( 'minus' ); - } else { - $( this ).parent().children( "span" ).removeClass( 'minus' ) - $( this ).parent().children( "span" ).addClass( 'plus' ); - } - } - } - }) - .live( "contextmenu", function( e ) { // Context Menu + .on( 'dragover dragenter', function( e ) { + e.preventDefault(); - _this.contextMenuShow( e, $( this ) - .attr( 'data-path' ), $( this ) - .attr( 'data-type' ), $( this ) - .html() ); - $( this ) - .addClass( 'context-menu-active' ); + e.stopPropagation(); + //console.log( 'dragover dragenter', e ); + }) + .on( 'dragleave dragend drop dragdrop', function( e ) { + + //e.preventDefault(); + //e.stopPropagation(); + _this.upload_overlay_off(); + }) + .on( 'drop dragdrop', async function( e ) { + + e.preventDefault(); + e.stopPropagation(); + + let d = e.originalEvent.dataTransfer; + + if( d ) { + + let files = []; + let items = []; + + let file_list = d.files; + let item_list = d.items; + let total_items = item_list.length; + + let c = $( ".uploads-content" ); + + if( item_list && item_list[0].webkitGetAsEntry ) { + + for( let i = 0;i < total_items;i++ ) { + + items.push( item_list[i].webkitGetAsEntry() ); + } + } + + console.log( files, items ); + + let project = $( '#file-manager a[data-type="root"]' ).attr( 'data-path' ); + let path = await _this.open_file_selector( project, {type: "directory"}, 1, {} ); + $( ".file-manager-selected" ).removeClass( "file-manager-selected" ); + + if( items.length ) { + + for( let i = items.length;i--; ) { + + let j = await _this.upload_load_files( items[i], path ); + files = files.concat( j ); + } + } else { + + files = file_list; + } + + _this.create_upload_nodes( files ); + + for( let i = files.length;i--; ) { + + let status = await _this.upload( files[i] ); + //console.log( status ); + } + console.log( 'drop', files, items ); + } else { + + codiad.message.error( i18n( 'Your browser does not seem to support dragging and dropping files' ) ); + } + }); + + $( '.drop-overlay' ).on( 'click', _this.upload_overlay_off ); + + $( ".uploads-container" ).resizable({ + handles: "n, e, w, ne, nw", + }); + $( "#uploads-button" ).on( 'click' , _this.toggle_uploads ); + $( "#uploads-close" ).on( 'click', function() { + + $( ".uploads-container" ).slideUp(); + }); + + amplify.subscribe( 'editor.changeCursor', function() { + + $( "#uploads-button" ).css( "right", ( $( "#cursor-position" ).width() + 36 ) + 'px' ); }); }, + archive: function( path ) { + + let _this = this; + + $.get( _this.controller + '?action=archive&path=' + encodeURIComponent( path ), function( data ) { + + console.log( data ); + let response = codiad.jsend.parse( data ); + parent = path.split( '/' ); + parent.pop(); + _this.rescan( parent.join( '/' ) ); + console.log( response ); + }); + }, + + calculate_upload_variables: async function() { + + let _this = codiad.filemanager; + let result = await codiad.system.get_ini_setting( 'post_max_size' ); + result = result.toLowerCase() + + console.log( result, result.includes( 'g' ), result.includes( 'm' ), result.includes( 'k' ) ); + + if( result.includes( 'g' ) ) { + + let integer = result.replace( /^\D+/g, '' ); + + console.log( integer, 1024*1024*1024*integer ); + result = 1024*1024*1024*integer; + } else if( result.includes( 'm' ) ) { + + let integer = result.replace( /^\D+/g, '' ); + console.log( integer, 1024*1024*integer ); + result = 1024*1024*integer; + } else if( result.includes( 'k' ) ) { + + let integer = result.replace( /^\D+/g, '' ); + console.log( integer, 1024*integer ); + result = 1024*integer; + } + + _this.post_max_size = result; + console.log( _this.post_max_size ); + }, + + context_menu_track_mouse: function( e ) { + + let _this = codiad.filemanager; + let offset = $( '#context-menu' ).offset(); + let bottom = offset.top + $( '#context-menu' ).outerHeight( true ) + 20; + let left = offset.left - 20; + let right = offset.left + $( '#context-menu' ).outerWidth( true ) + 20; + let top = offset.top - 20; + + if( ( e.clientX > right || e.clientX < left ) || ( e.clientY > bottom || e.clientY < top ) ) { + + $( '#file-manager, #editor-region' ).off( 'mousemove', codiad.filemanager.context_menu_track_mouse ); + $( '#context-menu, #editor-region' ).off( 'paste', codiad.editor.paste ); + _this.hide_context_menu(); + } + }, + ////////////////////////////////////////////////////////////////// - // Context Menu + // Copy to Clipboard ////////////////////////////////////////////////////////////////// - contextMenuShow: function( e, path, type, name ) { + copy_node: function( path ) { + + this.clipboard = path; + codiad.message.success( i18n( 'Copied to Clipboard' ) ); + }, + + create_node: function( path, type ) { + + codiad.modal.load( 250, this.dialog, { + action: 'create', + type: type, + path: path + }) + .then( function( container ) { + + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + e.stopPropagation(); + + let shortName = $( '#modal-content form input[name="object_name"]' ).val(); + let path = $( '#modal-content form input[name="path"]' ).val(); + let type = $( '#modal-content form input[name="type"]' ).val(); + let createPath = path + '/' + shortName; + + $.get( codiad.filemanager.controller + '?action=create&path=' + encodeURIComponent( createPath ) + '&type=' + type, function( data ) { + + let createResponse = codiad.jsend.parse( data ); + if( createResponse != 'error' ) { + + codiad.message.success( type.charAt( 0 ) + .toUpperCase() + type.slice( 1 ) + ' Created' ); + codiad.modal.unload(); + + if( type == 'file' ) { + + codiad.filemanager.openFile( createPath, true ); + } + codiad.filemanager.rescan( path ); + + /* Notify listeners. */ + amplify.publish( 'filemanager.onCreate', { + createPath: createPath, + path: path, + shortName: shortName, + type: type + }); + } + }); + }); + }); + }, + + create_upload_nodes: function( files ) { + + let c = $( ".uploads-content" ); + + for( let i = files.length;i--; ) { + + //Add files to gui and then wait for progress + + console.log( "Upload - GUI", files[i] ); + + let div = $( `
` ); + let title = $( `
${files[i].name}
` ); + let progress = $( `
` ); + let bar = $( `
` ); + let progress_text = $( `
0%
` ); + let span = $( `` ); + let bottom_span = $( `` ); + + let cancel = $( `
Cancel
` ); + let dismiss = $( `
Dismiss
` ); + + cancel.on( "click", function( e ) { + + files[i].cancel = true; + }); + + dismiss.on( "click", function( e ) { + + console.log( files[i], e ); + + if( files[i].finished === true ) { + + $( `div.upload-container[data-path="${files[i].path}"]` ).slideUp(); + } + }); + + span.append( title ); + span.append( progress_text ); + + progress.append( bar ); + + bottom_span.append( cancel ); + bottom_span.append( dismiss ); + + div.append( span ); + div.append( progress ); + div.append( bottom_span ); + c.append( div ); + } + + $( '.uploads-container' ).slideDown(); + }, + + delete_node: function( path ) { + let _this = this; + codiad.modal.load( 400, this.dialog, { + action: 'delete', + path: path + }) + .then( function( container ) { + + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + $.get( _this.controller + '?action=delete&path=' + encodeURIComponent( path ), function( data ) { + + let response = codiad.jsend.parse( data ); + if( response != 'error' ) { + + 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 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 ) { + + let _this = this; + codiad.modal.load( 400, this.dialog, { + action: 'delete', + path: path + }) + .then( function() { + + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + $.get( _this.controller + '?action=delete_children&path=' + encodeURIComponent( path ), function( data ) { + + console.log( data ); + let response = codiad.jsend.parse( data ); + if( response != 'error' ) { + + let node = $( '#file-manager a[data-path="' + path + '"]' ); + let parent_path = node.parent().parent().prev().attr( 'data-path' ); + + _this.toggle_directory( node, [], [], true ); + node.parent( 'li' ).children( 'span' ).remove(); + + // Close any active files + $( '#active-files a' ).each( function() { + + let curPath = $( this ).attr( 'data-path' ); + + console.log( curPath, curPath.indexOf( path ) ); + + if( path.indexOf( curPath ) == 0 ) { + + codiad.active.remove( curPath ); + } + }); + + /* Notify listeners. */ + amplify.publish( 'filemanager.onDelete', { + path: path + }); + } + codiad.modal.unload(); + }); + }); + }); + }, + + display_context_menu: function( e, path, type, name ) { + + let _this = this; + let top = e.pageY; $( '#context-menu a, #context-menu hr' ).hide(); + // Selective options switch ( type ) { + case 'directory': + $( '#context-menu .directory-only, #context-menu .non-root, #context-menu .both' ).show(); - break; + break; + case 'file': + $( '#context-menu .file-only, #context-menu .non-root, #context-menu .both' ).show(); - break; + break; + case 'root': + $( '#context-menu .directory-only, #context-menu .root-only' ).show(); - break; + $( '#context-menu .non-root' ).hide(); + break; + case 'editor': + $( '#context-menu .editor-only' ).show(); - break; + break; } + if( codiad.project.isAbsPath( $( '#file-manager a[data-type="root"]' ).attr( 'data-path' ) ) ) { + $( '#context-menu .no-external' ).hide(); } else if( type == "editor" ) { + $( '#context-menu .no-external' ).hide(); } else { + $( '#context-menu .no-external' ).show(); } + // Show menu - var top = e.pageY; + if( top > $( window ).height() - $( '#context-menu' ).height() ) { + top -= $( '#context-menu' ).height(); } + if( top < 10 ) { + top = 10; } - var max = $( window ).height() - top - 10; + let max = $( window ).height() - top - 10; $( '#context-menu' ) .css( { @@ -223,21 +593,26 @@ .attr( 'data-path', path ) .attr( 'data-type', type ) .attr( 'data-name', name ); + // Show faded 'paste' if nothing in clipboard if( this.clipboard === '' ) { + $( '#context-menu a[content="Paste"]' ) .addClass( 'disabled' ); } else { + $( '#context-menu a[data-action="paste"]' ) .removeClass( 'disabled' ); } + // Hide menu /** * make sure that the user has moved their mouse far enough * away from the context menu to warrant a close. */ - $( '#file-manager, #editor-region' ).on( 'mousemove', codiad.filemanager.contextCheckMouse ); + $( '#file-manager, #editor-region' ).on( 'mousemove', codiad.filemanager.context_menu_track_mouse ); $( '#context-menu, #editor-region' ).on( 'paste', codiad.editor.paste ); + $( '#context-menu, #editor-region' ).on( 'click', _this.hide_context_menu ); /* Notify listeners. */ amplify.publish( 'context-menu.onShow', { @@ -245,61 +620,125 @@ path: path, type: type }); + // Hide on click - $( '#context-menu a' ) - .click( function() { - _this.contextMenuHide(); + $( '#context-menu a' ).on( 'click', _this.hide_context_menu ); + }, + + download: function( path ) { + + let type = this.getType( path ); + $( '#download' ) + .attr( 'src', 'components/filemanager/download.php?path=' + encodeURIComponent( path ) + '&type=' + type ); + + }, + + get_extension: function( path ) { + + return path.split( '.' ).pop(); + }, + + get_index: function( path, files ) { + + let _this = codiad.filemanager; + + if( ! files ) { + + files = _this.files; + } + + return new Promise( async function( resolve, reject ) { + + let index = {}; + let total = ( !!files ) ? files.length : 0; + + for( let i = 0;i < total;i++ ) { + + if( path == files[i].dirname ) { + + index = files[i]; + break; + } else { + + if( files[i].children !== undefined && files[i].children !== null ) { + + //console.log( path ); + //console.log( files[i] ); + + index = await _this.get_index( path, files[i].children ); + + if( Object.keys( index ).length > 0 ) { + + break; + } + } + } + } + resolve( index ); }); }, - contextCheckMouse: function( e ) { + get_indexes: async function( path ) { - let offset = $( '#context-menu' ).offset(); - let bottom = offset.top + $( '#context-menu' ).outerHeight( true ) + 20; - let left = offset.left - 20; - let right = offset.left + $( '#context-menu' ).outerWidth( true ) + 20; - let top = offset.top - 20; + let r = await $.get( this.controller + '?action=index&path=' + encodeURIComponent( path ) ); + return r; + }, + + get_opened_indexes: async function( files ) { - if( ( e.clientX > right || e.clientX < left ) || ( e.clientY > bottom || e.clientY < top ) ) { + let _this = codiad.filemanager; + + for( let i = files.length;i--; ) { - $( '#file-manager, #editor-region' ).off( 'mousemove', codiad.filemanager.contextCheckMouse ); - $( '#context-menu, #editor-region' ).off( 'paste', codiad.editor.paste ); - codiad.filemanager.contextMenuHide(); + files[i].name = files[i].path; + + if( files[i].type == "directory" ) { + + let existing_data = await _this.get_index( files[i].path ); + + //console.log( "opened?", existing_data, files[i] ); + + if( existing_data.open ) { + + files[i].open = true; + let data = await _this.get_indexes( files[i].path ); + let response = codiad.jsend.parse( data ); + _this.set_children( files[i].path, files, response.index ); + + let children = await _this.get_opened_indexes( response.index ); + } else { + + files[i].open = false; + } + } } + return files; }, - contextMenuHide: function() { - $( '#context-menu' ) - .fadeOut( 200 ); - $( '#file-manager a' ) - .removeClass( 'context-menu-active' ); - /* Notify listeners. */ - amplify.publish( 'context-menu.onHide' ); + get_parent: function( path ) { + + let parent = path.split( '/' ); + parent.pop(); + return parent.join( '/' ); }, - ////////////////////////////////////////////////////////////////// - // Return the node name (sans path) - ////////////////////////////////////////////////////////////////// - - getShortName: function( path ) { - return path.split( '/' ) - .pop(); + get_file_reader: function() { + + let _this = codiad.filemanager; + + if( ! _this.reader ) { + + _this.reader = new FileReader(); + } + return _this.reader; }, - ////////////////////////////////////////////////////////////////// - // Return extension - ////////////////////////////////////////////////////////////////// - - getExtension: function( path ) { - return path.split( '.' ) - .pop(); + get_short_name: function( path ) { + + return path.split( '/' ).pop(); }, - ////////////////////////////////////////////////////////////////// - // Return type - ////////////////////////////////////////////////////////////////// - - getType: function( path ) { + get_type: function( path ) { if( path.match( /\\/g ) ) { @@ -309,221 +748,631 @@ return $( '#file-manager a[data-path="' + path + '"]' ).attr( 'data-type' ); }, - ////////////////////////////////////////////////////////////////// - // Create node in file tree - ////////////////////////////////////////////////////////////////// - - createObject: function( parent, path, type ) { - // NODE FORMAT:
  • {short_name}
  • - var parentNode = $( '#file-manager a[data-path="' + parent + '"]' ); - if( !$( '#file-manager a[data-path="' + path + '"]' ) - .length ) { // Doesn't already exist - if( parentNode.hasClass( 'open' ) && parentNode.hasClass( 'directory' ) ) { // Only append node if parent is open (and a directory) - var shortName = this.getShortName( path ); - if( type == 'directory' ) { - var appendage = '
  • ' + shortName + '
  • '; - } else { - var appendage = '
  • ' + shortName + '
  • '; - } - if( parentNode.siblings( 'ul' ) - .length ) { // UL exists, other children to play with - parentNode.siblings( 'ul' ) - .append( appendage ); - } else { - $( '' ) - .insertAfter( parentNode ); - } - } else { - parentNode.parent().children( 'span' ).removeClass( 'none' ); - parentNode.parent().children( 'span' ).addClass( 'plus' ); - } - } + hide_context_menu: function() { + + $( '#context-menu' ).fadeOut( 200 ); + $( '#file-manager a' ).removeClass( 'context-menu-active' ); + + /* Notify listeners. */ + amplify.publish( 'context-menu.onHide' ); }, - ////////////////////////////////////////////////////////////////// - // Loop out all files and folders in directory path - ////////////////////////////////////////////////////////////////// - - indexFiles: [], - - index: function( path, rescan ) { - let _this = this; - if( rescan === undefined ) { - rescan = false; - } - node = $( '#file-manager a[data-path="' + path + '"]' ); - if( node.hasClass( 'open' ) && !rescan ) { - node.parent( 'li' ) - .children( 'ul' ) - .slideUp( 300, function() { - $( this ) - .remove(); - node.removeClass( 'open' ); - }); - } else { + index: async function( path, rescan = false, node = null, filters = {}, callbacks = {} ) { + + path.replace( /\/$/, '' ); + + let _this = codiad.filemanager; + let children = 0; + let container = $( '' ); + let files = []; + let root = false; + let total_saved = _this.files.length; + let file = await _this.get_index( path ); + rescan = !!rescan; + + if( node === null ) { - node.addClass( 'loading' ); - $.get( this.controller + '?action=index&path=' + encodeURIComponent( path ), function( data ) { - - node.addClass( 'open' ); - var objectsResponse = codiad.jsend.parse( data ); - if( objectsResponse != 'error' ) { - - /* Notify listener */ - _this.indexFiles = objectsResponse.index; - amplify.publish( "filemanager.onIndex", { - path: path, - files: _this.indexFiles - }); - var files = _this.indexFiles; - if( files.length > 0 ) { - - if( node.parent().children( 'span' ).hasClass( 'plus' ) ) { - - node.parent().children( 'span' ).removeClass( 'plus' ).addClass( 'minus' ); - } - var display = 'display:none;'; - if( rescan ) { - - display = ''; - } - var appendage = ''; - if( rescan ) { - - node.parent( 'li' ) - .children( 'ul' ) - .remove(); - } - $( appendage ) - .insertAfter( node ); - if( !rescan ) { - - node.siblings( 'ul' ) - .slideDown( 300 ); - } - } - } - node.removeClass( 'loading' ); - if( rescan && _this.rescanChildren.length > _this.rescanCounter ) { - - _this.rescan( _this.rescanChildren[_this.rescanCounter++] ); - } else { - - _this.rescanChildren = []; - _this.rescanCounter = 0; - } - }); - } - }, - - rescanChildren: [], - - rescanCounter: 0, - - rescan: function( path ) { - let _this = this; - if( this.rescanCounter === 0 ) { - // Create array of open directories node = $( '#file-manager a[data-path="' + path + '"]' ); - node.parent().find( 'a.open' ).each( function() { - _this.rescanChildren.push( $( this ).attr( 'data-path' ) ); + } + + let parentNode = node.parent(); + let span = node.prev(); + + root = ( node.attr( 'data-type' ) == "root" ); + + if( root ) { + + node.addClass( "open" ); + node.droppable({ + accept: _this.node.accept, + drop: _this.node.drop, + over: _this.node.over, + out: _this.node.out }); } - this.index( path, true ); + if( ! callbacks.directory ) { + + callbacks.directory = [_this.index_directory_callback]; + } + + if( ! callbacks.file ) { + + callbacks.file = [_this.index_file_callback]; + } + + node.addClass( 'loading' ); + + if( Object.keys( file ).length == 0 ) { + + children = file.children; + } + + //console.log( file.children, file ) + + if( rescan || total_saved == 0 || ! children ) { + + let data = await _this.get_indexes( path ); + + //console.log( data ); + + let response = codiad.jsend.parse( data ); + let result = []; + + if( response != 'error' ) { + + result = response.index; + } + + files = result; + } else { + + files = file.children; + } + + files = await _this.get_opened_indexes( files ); + + if( total_saved == 0 || ( root && rescan ) ) { + + _this.files = files; + } else { + + _this.set_children( path, _this.files, files ); + } + + //console.log( _this.files, files ) + + _this.index_nodes( + path, + node, + files, + filters, + callbacks, + ); + + /* Notify listener */ + amplify.publish( "filemanager.onIndex", { + path: path, + files: files + }); + return files; }, - ////////////////////////////////////////////////////////////////// - // Open File - ////////////////////////////////////////////////////////////////// + index_directory_callback: function( entry, container, i, files ) { + + let _this = codiad.filemanager; + entry.children( 'a' ).droppable({ + accept: _this.node.accept, + drop: _this.node.drop, + over: _this.node.over, + out: _this.node.out, + + start: _this.node.start, + stop: _this.node.stop, + }); + entry.draggable({ + opacity: 0.85, + revert: true, + start: _this.node.start, + stop: _this.node.stop, + zIndex: 100, + }); + }, - openFile: function( path, focus ) { + index_file_callback: function( entry, container, i, files ) { + + let _this = codiad.filemanager; + entry.draggable({ + opacity: 0.85, + revert: true, + start: _this.node.start, + stop: _this.node.stop, + zIndex: 100, + }); + }, + + index_nodes: function( path, node, files, filters, callbacks ) { + + let _this = codiad.filemanager; + let container = $( '' ); + let total_files = files.length; + let parent = node.parent(); + let ul = parent.children( 'ul' ); + + for( let i = 0;i < total_files;i++ ) { + + let v = files[i]; + let ext = ''; + let name = ''; + let node_class = 'none'; + let entry = $( "
  • " ); + let span = $( "" ); + let link = $( "" ); + let type = null; + + entry.append( span, link ); + + if( v.type == "file" ) { + + if( filters.type == "directory" ) { + + continue; + } + + ext = "ext-" + v.extension; + name = v.basename; + type = 'file'; + link.addClass( ext ); + + } else if( v.type == "directory" ) { + + if( filters.type == "file" ) { + + continue; + } + + if( v.children ) { + + if( v.open ) { + + node_class = "minus"; + console.log( "loading children", v.path, link, v.children, filters, callbacks, link.parent() ); + link.addClass( "open" ); + _this.index_nodes( v.path, link, v.children, filters, callbacks ); + } else { + + node_class = "plus"; + } + } + + name = v.basename; + type = 'directory'; + } + + //console.log( v.path, v.type ); + + span.addClass( node_class ); + link.addClass( type ); + link.attr( "data-type", type ); + link.attr( "data-path", v.path ); + link.text( name ); + + container.append( entry ); + + if( typeof callbacks == "function" ) { + + callbacks( entry, container, v, files ); + } else if( Array.isArray( callbacks ) ) { + + let total_callbacks = callbacks.length; + for( let j = 0;j < total_callbacks;j++ ) { + + callbacks[j]( entry, container, v, files ); + } + } else if( callbacks === Object( callbacks ) ) { + + if( typeof callbacks[v.type] == "function" ) { + + callbacks[v.type]( entry, container, v, files ); + } else if( Array.isArray( callbacks[v.type] ) ) { + + let total_callbacks = callbacks[v.type].length; + for( let j = 0;j < total_callbacks;j++ ) { + + callbacks[v.type][j]( entry, container, v, files ); + } + } + } + } + + if( ul.length ) { + + ul.replaceWith( container ); + } else { + + container.insertAfter( node ); + } + + node.removeClass( 'loading' ); + }, + + is_child: function( parent, child ) { + + if( child === parent ) { + + return false; + } + + let parentTokens = parent.split( '/' ).filter( i => i.length ); + return parentTokens.every( ( t, i ) => child.split( '/' )[i] === t ) + }, + + node_listeners: function() { + + let _this = this; + + $( '#file-manager' ) + .on( 'click', 'a', function() { + + // Open or Expand + if( codiad.editor.settings.fileManagerTrigger ) { + + if( $( this ).hasClass( 'directory' ) ) { + + _this.toggle_directory( $( this ) ); + } else { + + _this.open_file( $( this ).attr( 'data-path' ) ); + } + } + }) + .on( 'click', 'span', function() { + + // Open or Expand + if( $( this ).parent().children( "a" ).attr( 'data-type' ) == 'directory' ) { + + _this.toggle_directory( $( this ).parent().children( "a" ) ); + } else { + + _this.open_file( $( this ).parent().children( "a" ).attr( 'data-path' ) ); + } + }) + .on( "contextmenu", 'a', function( e ) { + + // Context Menu + e.preventDefault(); + _this.display_context_menu( + e, + $( this ).attr( 'data-path' ), + $( this ).attr( 'data-type' ), + $( this ).html() + ); + $( this ).addClass( 'context-menu-active' ); + }) + .on( 'dblclick', 'a', function() { + + // Open or Expand + if( ! codiad.editor.settings.fileManagerTrigger ) { + + if( $( this ).hasClass( 'directory' ) ) { + + _this.toggle_directory( $( this ) ); + } else { + + _this.open_file( $( this ).attr( 'data-path' ) ); + } + } + }) + .on( 'selectstart', false ); + }, + + open_file: function( path, focus = true ) { /* Notify listeners. */ amplify.publish( 'filemanager.onFileWillOpen', { path: path }); - if( focus === undefined ) { - focus = true; - } - var node = $( '#file-manager a[data-path="' + path + '"]' ); - var ext = this.getExtension( path ); - if( $.inArray( ext.toLowerCase(), this.noOpen ) < 0 ) { + let _this = codiad.filemanager; + let node = $( '#file-manager a[data-path="' + path + '"]' ); + let ext = _this.get_extension( path ); + let do_not_open = []; + + $.each( _this.file_preview_list, function( id, value ) { + + console.log( "testing", id, value ); + do_not_open = do_not_open.concat( value ); + }); + + console.log( "open file checks", ext, do_not_open, $.inArray( ext.toLowerCase(), do_not_open ) ); + + if( $.inArray( ext.toLowerCase(), do_not_open ) < 0 ) { + node.addClass( 'loading' ); - $.get( this.controller + '?action=open&path=' + encodeURIComponent( path ), function( data ) { - var openResponse = codiad.jsend.parse( data ); + $.get( _this.controller + '?action=open&path=' + encodeURIComponent( path ), function( data ) { + + let openResponse = codiad.jsend.parse( data ); if( openResponse != 'error' ) { + node.removeClass( 'loading' ); - codiad.active.open( path, openResponse.content, openResponse.mtime, false, focus ); + codiad.active.open( path, openResponse.content, openResponse.mtime, false, focus, openResponse.read_only ); } }); } else { - if( !codiad.project.isAbsPath( path ) ) { - if( $.inArray( ext.toLowerCase(), this.noBrowser ) < 0 ) { - this.download( path ); + + if( ! codiad.project.isAbsPath( path ) ) { + + let preview = []; + preview = preview.concat( _this.file_preview_list.audio ); + preview = preview.concat( _this.file_preview_list.image ); + + console.log( "preview file checks", ext, preview, $.inArray( ext.toLowerCase(), preview ) ); + + if( $.inArray( ext.toLowerCase(), preview ) < 0 ) { + + _this.download( path ); } else { - this.openInModal( path ); + + _this.open_in_modal( path ); } } else { + codiad.message.error( i18n( 'Unable to open file in Browser while using absolute path.' ) ); } } }, - ////////////////////////////////////////////////////////////////// - // Open in browser - ////////////////////////////////////////////////////////////////// - - openInBrowser: function( path ) { + open_file_selector: function( path, filters = {}, limit = 1, callbacks ) { let _this = this; - - $.ajax( { - url: this.controller + '?action=open_in_browser&path=' + encodeURIComponent( path ), - success: function( data ) { - var openIBResponse = codiad.jsend.parse( data ); - if( openIBResponse != 'error' ) { - - _this.preview = window.open( openIBResponse.url, '_newtab' ); - - let editor = codiad.editor.getActive(); - - if( _this.auto_reload && editor !== null ) { - - codiad.editor.getActive().addEventListener( "change", _this.refreshPreview ); - } - - + return new Promise( function( resolve, reject ) { + + codiad.modal.load( + 300, + _this.dialog, + { + action: 'selector', } - }, - async: false + ) + .then( async function( container ) { + + let _this = codiad.filemanager; + let div = null; + let select = $( '' ); + let cancel = $( '' ); + + if( ! path ) { + + path = codiad.project.getCurrent(); + } + + if( Object.keys( filters ).length == 0 ) { + + filters = { + + type: 'directory', + } + } + + div = $( '#file-manager a[data-path="' + path + '"]' ).parent().parent().parent().clone(); + let node = $( div ).find( 'a[data-path="' + path + '"]' ); + + console.log( div, node ); + + node.off(); + node.children().off(); + + _this.selector_listeners( div, limit, filters, callbacks ); + + let result = await _this.index( + path, + false, + node, + filters, + callbacks, + ); + + console.log( result ); + + container.html( div ); + container.append( select ); + container.append( cancel ); + + select.on( 'click', function( e ) { + + codiad.modal.unload(); + resolve( _this.selected ); + }); + cancel.on( 'click', function( e ) { + + codiad.modal.unload(); + reject({ + status: "error", + message: "User canceled action." + }); + }); + }) + .catch( reject ); }); }, - refreshPreview: function( event ) { + open_in_modal: function( path ) { + + let type = ""; + let ext = this.getExtension( path ).toLowerCase(); + + if( this.file_preview_list.audio.includes( ext ) ) { + + type = 'music_preview'; + } else if( this.file_preview_list.image.includes( ext ) ) { + + type = 'preview'; + } + + codiad.modal.load( 250, this.dialog, { + action: type, + path: path + }); + }, + + open_rename: function( path ) { + + let _this = codiad.filemanager; + let shortName = this.get_short_name( path ); + let type = this.getType( path ); + + codiad.modal.load( 250, this.dialog, { + action: 'rename', + path: path, + short_name: shortName, + type: type + }) + .then( function( content ) { + + $( content ).find( 'form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + + let new_name = $( '#modal-content form input[name="object_name"]' ).val(); + let arr = path.split( '/' ); + let temp = new Array(); + for( i = 0; i < arr.length - 1; i++ ) { + temp.push( arr[i] ) + } + let new_path = temp.join( '/' ) + '/' + new_name; + _this.rename_node( path, new_path ); + codiad.modal.unload(); + }); + }); + }, + + paste_node: function( path ) { + + let _this = this; + let replace = false; + let clipboard = this.clipboard; + console.log( path, clipboard ) + + if( clipboard == '' ) { + + codiad.message.error( i18n( 'Nothing in Your Clipboard' ) ); + } else if( path == clipboard ) { + + codiad.message.error( i18n( 'Cannot Paste Directory Into Itself' ) ); + } else { + + let short_name = _this.get_short_name( clipboard ); + let new_path = path + '/' + short_name + let existing_node = $( '#file-manager a[data-path="' + new_path + '"]' ); + + console.log( existing_node ); + + if( existing_node.length ) { + + // Confirm replace? + codiad.modal.load( 400, this.dialog, { + action: 'replace', + path: new_path + }) + .then( function( container ) { + + $( '#modal-content form' ) + .on( 'submit', function( e ) { + + e.preventDefault(); + codiad.modal.unload(); + + if( $( '#modal-content form select[name="or_action"]' ).val() == 1 ) { + + replace = true; + } + + $.ajax({ + type: 'POST', + url: _this.controller + '?action=copy', + data: { + + path: clipboard, + destination: new_path, + replace: replace, + }, + 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: clipboard, + destination: new_path, + replace: replace, + }, + 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; @@ -541,433 +1390,231 @@ } _this.refresh_interval = setTimeout( function() { - if( _this.preview == null ) { + if( _this.preview_window == null ) { return; } try { - if( ( typeof _this.preview.location.reload ) == "undefined" ) { + if( ( typeof _this.preview_window.location.reload ) == "undefined" ) { - _this.preview = null; - codiad.editor.getActive().removeEventListener( "change", _this.refreshPreview ); + _this.preview_window = null; + codiad.editor.getActive().removeEventListener( "change", _this.refresh_preview ); return; } - _this.preview.location.reload( true ); + _this.preview_window.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 ); + _this.preview_window = null; + codiad.editor.getActive().removeEventListener( "change", _this.refresh_preview ); } - }, 500 ); + }, 1000 ); }, - openInModal: function( path ) { + rename: function( path, new_path ) { - let type = ""; - var ext = this.getExtension( path ).toLowerCase(); + let _this = codiad.filemanager; - if( this.noAudio.includes( ext ) ) { + return new Promise( function( resolve, reject ) { - type = 'music_preview'; - } else if( this.noImages.includes( ext ) ) { - - type = 'preview'; - } - - codiad.modal.load( 250, this.dialog, { - action: type, - path: path - }); - }, - saveModifications: function( path, data, callbacks, save = true ) { - - callbacks = callbacks || {}; - let _this = this, action; - var notifySaveErr = function() { - codiad.message.error( i18n( 'File could not be saved' ) ); - if( typeof callbacks.error === 'function' ) { - var context = callbacks.context || _this; - callbacks.error.apply( context, [data] ); - } - } - $.post( this.controller + '?action=modify&path=' + encodeURIComponent( path ), data, function( resp ) { - resp = $.parseJSON( resp ); - if( resp.status == 'success' ) { - if( save === true ) { - codiad.message.success( i18n( 'File saved' ) ); - } - if( typeof callbacks.success === 'function' ) { - var context = callbacks.context || _this; - callbacks.success.call( context, resp.data.mtime ); - } - } else { - if( resp.message == 'Client is out of sync' ) { - var 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 { - var session = codiad.editor.getActive().getSession(); - session.serverMTime = null; - session.untainted = null; - } - } else codiad.message.error( i18n( 'File could not be saved' ) ); - if( typeof callbacks.error === 'function' ) { - var context = callbacks.context || _this; - callbacks.error.apply( context, [resp.data] ); - } - } - }).error( notifySaveErr ); - }, - ////////////////////////////////////////////////////////////////// - // Save file - ////////////////////////////////////////////////////////////////// - - saveFile: function( path, content, callbacks, save = true ) { - this.saveModifications( path, { - content: content - }, callbacks, save ); - }, - - savePatch: function( path, patch, mtime, callbacks, alerts ) { - if( patch.length > 0 ) - this.saveModifications( path, { - patch: patch, - mtime: mtime - }, callbacks, alerts ); - else if( typeof callbacks.success === 'function' ) { - var context = callbacks.context || this; - callbacks.success.call( context, mtime ); - } - }, - - ////////////////////////////////////////////////////////////////// - // Create Object - ////////////////////////////////////////////////////////////////// - - createNode: function( path, type ) { - codiad.modal.load( 250, this.dialog, { - action: 'create', - type: type, - path: path - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { - e.preventDefault(); - var shortName = $( '#modal-content form input[name="object_name"]' ) - .val(); - var path = $( '#modal-content form input[name="path"]' ) - .val(); - var type = $( '#modal-content form input[name="type"]' ) - .val(); - var createPath = path + '/' + shortName; - $.get( codiad.filemanager.controller + '?action=create&path=' + encodeURIComponent( createPath ) + '&type=' + type, function( data ) { - var createResponse = codiad.jsend.parse( data ); - if( createResponse != 'error' ) { - codiad.message.success( type.charAt( 0 ) - .toUpperCase() + type.slice( 1 ) + ' Created' ); - codiad.modal.unload(); - // Add new element to filemanager screen - codiad.filemanager.createObject( path, createPath, type ); - if( type == 'file' ) { - codiad.filemanager.openFile( createPath, true ); - } + $.ajax({ + type: 'POST', + url: _this.controller + '?action=rename', + data: { - codiad.filemanager.rescan( path ); + path: path, + destination: new_path + }, + success: function( data ) { - /* Notify listeners. */ - amplify.publish( 'filemanager.onCreate', { - createPath: createPath, - path: path, - shortName: shortName, - type: type - }); - } + resolve( data ); + }, + error: function( data ) { + + reject( data ); + }, }); }); }, - ////////////////////////////////////////////////////////////////// - // Copy to Clipboard - ////////////////////////////////////////////////////////////////// - - copyNode: function( path ) { - this.clipboard = path; - codiad.message.success( i18n( 'Copied to Clipboard' ) ); - }, - - ////////////////////////////////////////////////////////////////// - // Paste - ////////////////////////////////////////////////////////////////// - - pasteNode: function( path ) { + rename_node: async function( path, new_path ) { + + let short_name = this.get_short_name( path ); + let type = this.getType( path ); let _this = this; - 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 project = codiad.project.getCurrent(); - var shortName = _this.getShortName( _this.clipboard ); - if( $( '#file-manager a[data-path="' + path + '/' + shortName + '"]' ) - .length ) { // Confirm overwrite? - codiad.modal.load( 400, this.dialog, { - action: 'overwrite', - path: path + '/' + shortName - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { - e.preventDefault(); - var duplicate = false; - if( $( '#modal-content form select[name="or_action"]' ).val() == 1 ) { - duplicate = true; - console.log( 'Dup!' ); - } - _this.processPasteNode( path, duplicate ); - }); - } else { // No conflicts; proceed... - _this.processPasteNode( path, false ); - } + let project = codiad.project.getCurrent(); + + let arr = path.split( '/' ); + let message = "Successfully Renamed." + let new_parent = new_path.split( '/' ); + let parent = path.split( '/' ); + let temp = []; + + parent.pop(); + new_parent.pop(); + + for( i = 0; i < arr.length - 1; i++ ) { - codiad.filemanager.rescan( project ); + temp.push( arr[i] ) + } + + let result = codiad.jsend.parse( await _this.rename( path, new_path ) ); + + if( result != 'error' ) { + + if( type !== undefined ) { + + let node = $( '#file-manager a[data-path="' + path + '"]' ); + + node.attr( 'data-path', new_path ).html( new_path.split( "/" ).pop() ); + message = type.charAt( 0 ).toUpperCase() + type.slice( 1 ) + ' Renamed' + codiad.message.success( message ); + + // Change icons for file + let current_class = 'ext-' + _this.get_extension( path ); + let new_class = 'ext-' + _this.get_extension( new_path ); + + $( '#file-manager a[data-path="' + new_path + '"]' ) + .removeClass( current_class ) + .addClass( new_class ); + codiad.active.rename( path, new_path ); + + codiad.filemanager.rescan( parent.join( '/' ) ); + + if( parent !== new_parent ) { + + console.log( parent, new_parent ); + codiad.filemanager.rescan( new_parent.join( '/' ) ); + } + + /* Notify listeners. */ + amplify.publish( 'filemanager.onRename', { + path: path, + newPath: new_path, + project: project + }); + } } }, - processPasteNode: function( path, duplicate ) { - let _this = this; - var shortName = this.getShortName( this.clipboard ); - var type = this.getType( this.clipboard ); + preview: function( path ) { - $.get( this.controller + '?action=duplicate&path=' + - encodeURIComponent( this.clipboard ) + '&destination=' + - encodeURIComponent( path + '/' + shortName ) + '&duplicate=' + encodeURIComponent( duplicate ), - function( data ) { - var pasteResponse = codiad.jsend.parse( data ); - if( pasteResponse != 'error' ) { - _this.createObject( path, path + '/' + shortName, type ); - codiad.modal.unload(); - /* Notify listeners. */ - amplify.publish( 'filemanager.onPaste', { - path: path, - shortName: shortName, - duplicate: duplicate - }); - } - }); - }, - - ////////////////////////////////////////////////////////////////// - // Rename - ////////////////////////////////////////////////////////////////// - - renameNode: function( path ) { - var shortName = this.getShortName( path ); - var type = this.getType( path ); let _this = this; - codiad.modal.load( 250, this.dialog, { - action: 'rename', - path: path, - short_name: shortName, - type: type - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { - let project = codiad.project.getCurrent(); - e.preventDefault(); - var newName = $( '#modal-content form input[name="object_name"]' ) - .val(); - // Build new path - var arr = path.split( '/' ); - var temp = new Array(); - for( i = 0; i < arr.length - 1; i++ ) { - temp.push( arr[i] ) - } - var newPath = temp.join( '/' ) + '/' + newName; - $.get( _this.controller, { - action: 'modify', - path: path, - new_name: newName - }, function( data ) { - var renameResponse = codiad.jsend.parse( data ); - let renamedMessage = ""; - if( renameResponse != 'error' ) { + + $.ajax({ + type: 'GET', + url: _this.controller + '?action=preview&path=' + encodeURIComponent( path ), + success: function( data ) { + + console.log( data ); + let r = JSON.parse( data ); + + if( r.status === "success" ) { - if( type == undefined ) { + _this.preview_window = window.open( r.data, '_newtab' ); + + let editor = codiad.editor.getActive(); + + if( _this.auto_reload && editor !== null ) { - renamedMessage = 'Successfully Renamed' + editor.addEventListener( "change", _this.refresh_preview ); + } + } else { + + codiad.message.error( i18n( r.message ) ); + } + }, + error: function( data ) { + + codiad.message.error( i18n( r.message ) ); + }, + }); + }, + + rescan: function( path ) { + + let _this = codiad.filemanager; + return _this.index( path, true ); + }, + + 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( r ); + } 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( r ); } else { - renamedMessage = type.charAt( 0 ).toUpperCase() + type.slice( 1 ) + ' Renamed' + codiad.message.error( i18n( r.message ) ); + reject( data ); } + }, + error: function( data ) { - codiad.message.success( renamedMessage ); - var node = $( '#file-manager a[data-path="' + path + '"]' ); - // Change pathing and name for node - node.attr( 'data-path', newPath ) - .html( newName ); - if( type == 'file' ) { // Change icons for file - curExtClass = 'ext-' + _this.getExtension( path ); - newExtClass = 'ext-' + _this.getExtension( newPath ); - $( '#file-manager a[data-path="' + newPath + '"]' ) - .removeClass( curExtClass ) - .addClass( newExtClass ); - } else { // Change pathing on any sub-files/directories - _this.repathSubs( path, newPath ); - } - // Change any active files - codiad.active.rename( path, newPath ); - codiad.modal.unload(); - - let parent = path.split( '/' ); - parent.pop(); - codiad.filemanager.rescan( parent.join( '/' ) ); - /* Notify listeners. */ - amplify.publish( 'filemanager.onRename', { - path: path, - newPath: newPath, - project: project - }); - } + codiad.message.error( i18n( 'File could not be saved' ) ); + reject( data ); + }, }); }); }, - repathSubs: function( oldPath, newPath ) { - $( '#file-manager a[data-path="' + newPath + '"]' ) - .siblings( 'ul' ) - .find( 'a' ) - .each( function() { - // Hit the children, hit 'em hard - var curPath = $( this ) - .attr( 'data-path' ); - var revisedPath = curPath.replace( oldPath, newPath ); - $( this ) - .attr( 'data-path', revisedPath ); - }); - }, - - ////////////////////////////////////////////////////////////////// - // Delete - ////////////////////////////////////////////////////////////////// - - deleteNode: function( path ) { + search: async function( path ) { + let _this = this; - codiad.modal.load( 400, this.dialog, { - action: 'delete', - path: path - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { - e.preventDefault(); - $.get( _this.controller + '?action=delete&path=' + encodeURIComponent( path ), function( data ) { - var deleteResponse = codiad.jsend.parse( data ); - if( deleteResponse != 'error' ) { - var node = $( '#file-manager a[data-path="' + path + '"]' ); - let parent_path = node.parent().parent().prev().attr( 'data-path' ); - node.parent( 'li' ).remove(); - // Close any active files - $( '#active-files a' ) - .each( function() { - var curPath = $( this ) - .attr( 'data-path' ); - if( curPath.indexOf( path ) == 0 ) { - codiad.active.remove( curPath ); - } - }); - /* Notify listeners. */ - amplify.publish( 'filemanager.onDelete', { - deletePath: path, - path: parent_path - }); - } - codiad.modal.unload(); - }); - }); - }, - - deleteInnerNode: function( path ) { - let _this = this; - codiad.modal.load( 400, this.dialog, { - action: 'delete', - path: path - }); - $( '#modal-content form' ) - .live( 'submit', function( e ) { - e.preventDefault(); - $.get( _this.controller + '?action=deleteInner&path=' + encodeURIComponent( path ), function( data ) { - var deleteResponse = codiad.jsend.parse( data ); - if( deleteResponse != 'error' ) { - var node = $( '#file-manager a[data-path="' + path + '"]' ).parent( 'ul' ).remove(); - - // Close any active files - $( '#active-files a' ) - .each( function() { - var curPath = $( this ) - .attr( 'data-path' ); - if( curPath.indexOf( path ) == 0 ) { - codiad.active.remove( curPath ); - } - }); - - //Rescan Folder - node.parent() - .find( 'a.open' ) - .each( function() { - _this.rescanChildren.push( $( this ) - .attr( 'data-path' ) ); - }); - - /* Notify listeners. */ - amplify.publish( 'filemanager.onDelete', { - deletePath: path + "/*", - path: path - }); - } - codiad.modal.unload(); - }); - }); - }, - ////////////////////////////////////////////////////////////////// - // Search - ////////////////////////////////////////////////////////////////// - - search: function( path ) { - codiad.modal.load( 500, this.dialog, { + let container = await codiad.modal.load( 500, this.dialog, { action: 'search', path: path }); - codiad.modal.load_process.done( async function() { - var lastSearched = JSON.parse( await codiad.settings.get_option( "lastSearched" ) ); - if( lastSearched ) { - $( '#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 ); - } - } - }); codiad.modal.hideOverlay(); - let _this = this; $( '#modal-content form' ) - .live( 'submit', function( e ) { - $( '#filemanager-search-processing' ) - .show(); + .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(); + 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 @@ -975,12 +1622,21 @@ } searchType = $( '#modal-content form select[name="search_type"]' ) .val(); - $.post( _this.controller + '?action=search&path=' + encodeURIComponent( path ) + '&type=' + searchType, { - search_string: searchString, - search_file_type: searchFileType + let options = { + filetype: fileExtensions, + }; + $.post( _this.controller + '?action=search', { + path: path, + query: searchString, + options: JSON.stringify( options ) }, function( data ) { - searchResponse = codiad.jsend.parse( data ); - var results = ''; + + 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 @@ -998,44 +1654,628 @@ $( '#filemanager-search-results' ) .slideUp(); } - _this.saveSearchResults( searchString, searchType, fileExtensions, results ); $( '#filemanager-search-processing' ) .hide(); }); }); }, - ///////////////////////////////////////////////////////////////// - // saveSearchResults - ///////////////////////////////////////////////////////////////// - saveSearchResults: function( searchText, searchType, fileExtensions, searchResults ) { - var lastSearched = { - searchText: searchText, - searchType: searchType, - fileExtension: fileExtensions, - searchResults: searchResults - }; - localStorage.setItem( "lastSearched", JSON.stringify( lastSearched ) ); + set_children: function( path, files, children ) { + + let _this = this; + let index = {}; + let total = ( !!files ) ? files.length : 0; + + for( let i = 0;i < total;i++ ) { + + if( path == files[i].dirname ) { + + files[i].children = children; + index = files[i]; + break; + } else { + + if( files[i].children !== undefined ) { + + index = _this.set_children( path, files[i].children, children ); + + if( Object.keys( index ).length > 0 ) { + + break; + } + } + } + } + return index; }, - ////////////////////////////////////////////////////////////////// - // Upload - ////////////////////////////////////////////////////////////////// - uploadToNode: function( path ) { - codiad.modal.load( 500, this.dialogUpload, { - path: path + selector_listeners: function( node, limit, filters, callbacks ) { + + let _this = codiad.filemanager; + + $( node ) + .on( 'click', 'a', async function( e ) { + + let i = $( e.target ); + + // Select or Expand + if( codiad.editor.settings.fileManagerTrigger ) { + + _this.toggle_directory( i, filters, callbacks ); + } else { + + _this.toggle_select_node( $( e.target ), limit ); + } + }) + .on( 'click', 'span', async function( e ) { + + let i = $( e.target ).parent().children( 'a' ); + _this.toggle_directory( i, {type: 'directory'} ); + }) + .on( 'dblclick', 'a', async function( e ) { + + let i = $( e.target ); + + if( ! codiad.editor.settings.fileManagerTrigger ) { + + _this.toggle_directory( i, {type: 'directory'} ); + } else { + + _this.toggle_select_node( i, limit ); + } + }) + .on( 'selectstart', false ); + }, + + set_index: function( path, files, data ) { + + let _this = codiad.filemanager; + let index = false; + let total = ( !!files ) ? files.length : 0; + + for( let i = 0;i < total;i++ ) { + + if( path == files[i].dirname ) { + + files[i] = data; + index = files[i]; + break; + } else { + + if( files[i].children !== undefined ) { + + index = _this.set_index( path, files[i].children, data ); + + if( index ) { + + break; + } + } + } + } + return index; + }, + + toggle_directory: async function( node, filters, callbacks, open ) { + + let _this = codiad.filemanager; + + node.addClass( "loading" ); + let path = node.attr( "data-path" ); + let type = node.attr( "data-type" ); + let i = await _this.get_index( path, _this.files ); + let span = node.parent().children( 'span' ); + let link = node.parent().children( 'a' ); + + console.log( i ); + + if( Object.keys( i ).length == 0 ) { + + let result = await _this.index( _this.get_parent( path ), true ); + i = await _this.get_index( path, _this.files ); + } + + if( Object.keys( i ).length == 0 ) { + + i = { + path: path, + open: true, + } + } + + if( type == "root" ) { + + console.log( node.parent().children( 'ul' ).find( ".open" ) ); + + node.parent().children( 'ul' ).find( ".open" ).each( function( id, n ) { + + console.log( id, n ); + + let i = $( n ); + _this.toggle_directory( i, [], [], true ); + }); + + node.removeClass( "loading" ); + return; + } else if( i.open || open ) { + + node.parent().children( 'ul' ) + .slideUp( 300, function() { + + $( this ).remove(); + + span.removeClass( 'minus' ); + node.removeClass( 'open' ); + + span.addClass( 'plus' ); + + if( typeof close_callback == "function" ) { + + close_callback(); + } + }); + + if( typeof open_callback == "function" ) { + + close_callback( node ); + } + } else { + + span.removeClass( 'plus' ); + + span.addClass( 'minus' ); + link.addClass( 'open' ); + + _this.index( path ); + } + + i.open = !i.open + _this.set_index( path, _this.files, i ); + + console.log( i, await _this.get_index( path, _this.files ) ); + node.removeClass( "loading" ); + }, + + toggle_select_node: function( node, limit = 0 ) { + + let _this = codiad.filemanager; + let selected = false; + let path = node.attr( 'data-path' ); + let i = 1; + + for( i = _this.selected.length;i--; ) { + + if( _this.selected[i] == path ) { + + selected = true; + break; + } + } + + if( limit > 0 && _this.selected.length >= limit ) { + + $( ".file-manager-selected" ).removeClass( "file-manager-selected" ); + _this.selected = []; + _this.selected.push( path ); + $( `[data-path='${path}']` ).addClass( "file-manager-selected" ); + } else if( limit === -1 ) { + + $( ".file-manager-selected" ).removeClass( "file-manager-selected" ); + } else { + + if( selected ) { + + _this.selected.splice( i, 1 ); + $( ".file-manager-selected" ).removeClass( "file-manager-selected" ); + } else { + + _this.selected.push( path ); + $( `[data-path='${path}']` ).addClass( "file-manager-selected" ); + } + } + + console.log( path, _this.selected ); + }, + + toggle_uploads: function() { + + let b = $( "#uploads-button" ); + let c = $( ".uploads-container" ); + + if( c.css( "display" ) == "none" ) { + + c.slideDown(); + } else { + + c.slideUp(); + } + }, + + unarchive: function( path ) { + + let _this = this; + + $.get( _this.controller + '?action=unarchive&path=' + encodeURIComponent( path ), function( data ) { + + console.log( data ); + let response = codiad.jsend.parse( data ); + console.log( response ); + parent = path.split( '/' ); + parent.pop(); + _this.rescan( parent.join( '/' ) ); }); }, - ////////////////////////////////////////////////////////////////// - // Download - ////////////////////////////////////////////////////////////////// + upload: function( file ) { + + let _this = codiad.filemanager; + let blob_size = 1024*1024*4; + let total_size = file.size; + let upload_status = null; + let total_blobs = 0; + let current = 0; + let index = 0; + + let uc = $( `div.upload-container[data-path="${file.path}"]` ); + let upt = uc.find( ".upload-progress-text" ); + let upb = uc.find( ".bar" ); + console.log( file, file.size ); + + return new Promise( function( resolve, reject ) { + + if( total_size < blob_size ) { + + blob_size = total_size; + total_blobs = 1; + } else { + + total_blobs = ( Math.round( ( total_size / blob_size ) ) ); + } + + console.log( blob_size, _this.uploads.cache, total_size ); + + let timeout = setInterval( function() { + + let progress = Math.round( ( current / total_size ) * 100 ); + upt.text( progress + "%" ); + upb.css( "width", progress + "%" ); + + console.log( progress, uc, upt, upb ); + + if( file.cancel ) { + + /** + * If canceling we must make sure we clear out the cache + * so no leftovers are stored in memory, or screw up a + * future upload. + */ + _this.uploads.cache = []; + _this.upload_cancel( file.path ); + clearTimeout( timeout ); + + upt.css( "color", "red" ); + upb.css( "color", "red" ); + + upt.text( "Canceled" ); + + uc.find( ".upload-cancel" ).slideUp(); + + resolve( true ); + return; + } else if( ( index == total_blobs || current == total_size ) && _this.uploads.cache.length == 0 ) { + + _this.upload_stitch( file.path ); + file.finished = true; + clearTimeout( timeout ); + resolve( true ); + } else if( ( index != total_blobs && current != total_size ) && _this.uploads.cache.length < _this.uploads.max && ! file.cancel ) { + + console.log( "Adding new blob: ", ( index + 1 ) + "/" + total_blobs, current + blob_size + "/" + total_size ); + let reader = new FileReader(); + let blob = file.slice( current, current + blob_size ); + reader.onload = async function( e ) { + + current = current + blob_size; + _this.uploads.cache.push({ + + blob: e.target.result, + path: file.path, + status: "queued", + index: index, + }); + index++; + _this.upload_clear_cache(); + } + reader.readAsDataURL( blob ); + } else { + + _this.upload_clear_cache(); + } + }, 100 ); + + }); + }, - download: function( path ) { - var type = this.getType( path ); - $( '#download' ) - .attr( 'src', 'components/filemanager/download.php?path=' + encodeURIComponent( path ) + '&type=' + type ); - } + uploads: { + + cache: [], + max: 4, + }, + + upload_blob: function( blob, path, index, o ) { + + return new Promise( function( resolve, reject ) { + + let _this = codiad.filemanager; + let form = new FormData(); + + form.append( 'path', path ); + form.append( 'index', index ); + form.append( 'data', blob ); + + $.ajax({ + type: 'POST', + url: _this.controller + "?action=upload_blob", + data: form, + processData: false, + contentType: false, + success: function( data ) { + + + console.log( data ); + let d = JSON.parse( data ); + + if( d.status == "success" ) { + + o.status = "success"; + resolve( data ); + } else { + + o.status = "error"; + reject( data ); + } + }, + error: function( data ) { + + o.status = "error"; + reject( data ); + }, + }); + }); + }, + + upload_cancel: function( path ) { + + let _this = codiad.filemanager; + let form = new FormData(); + + form.append( 'path', path ); + + $.ajax({ + type: 'POST', + url: _this.controller + "?action=upload_cancel", + data: form, + processData: false, + contentType: false, + success: function( data ) { + + console.log( data ); + parent = path.split( '/' ); + + parent.pop(); + parent.pop(); + + console.log( path, parent.join( '/' ) ); + + _this.rescan( parent.join( '/' ) ); + }, + error: function( data ) { + + console.log( data ); + }, + }); + }, + + upload_clear_cache: function() { + + let _this = codiad.filemanager; + for( let i = _this.uploads.cache.length;i--; ) { + + if( _this.uploads.cache[i].status == "success" ) { + + _this.uploads.cache.splice( i, 1 ); + } else if( _this.uploads.cache[i].status == "error" ) { + + _this.uploads.cache[i].status = "retrying"; + _this.upload_blob( _this.uploads.cache[i].blob, _this.uploads.cache[i].path, _this.uploads.cache[i].index, _this.uploads.cache[i] ); + } else if( _this.uploads.cache[i].status == "queued" ) { + + _this.uploads.cache[i].status = "uploading"; + _this.upload_blob( _this.uploads.cache[i].blob, _this.uploads.cache[i].path, _this.uploads.cache[i].index, _this.uploads.cache[i] ); + } + } + }, + + upload_load_files: async function( item, path, files = null ) { + + let _this = codiad.filemanager; + + if( files === null ) { + + files = []; + } + + return new Promise( function( resolve, reject ) { + + if( item.isFile ) { + + // Get file + item.file( function( file ) { + + if( ( `${path}` ).charAt( path.length - 1 ) != "/" ) { + + path += "/"; + } + + file.path = path + file.name; + file.cancel = false; + file.finished = false; + + files.push( file ); + resolve( files ); + }); + } else if( item.isDirectory ) { + + // Get folder contents + let dirReader = item.createReader(); + dirReader.readEntries( async function( entries ) { + + if( ( `${path}` ).charAt( path.length - 1 ) != "/" ) { + + path += "/"; + } + + let total = entries.length; + for( let i = 0;i < total; i++ ) { + + let children = await _this.upload_load_files( entries[i], path + item.name + "/", files ); + files.concat( children ); + } + resolve( files ); + }, reject ); + } else if( item.name ) { + + if( ( `${path}` ).charAt( path.length - 1 ) != "/" ) { + + path += "/"; + } + + item.path = path + item.name; + item.cancel = false; + item.finished = false; + + files.push( item ); + resolve( files ); + } else { + + resolve( files ); + } + }); + }, + + upload_overlay_off: function() { + + $( '.drop-overlay' ).css( 'display', 'none' ); + }, + + upload_overlay_on: function( e ) { + + $( '.drop-overlay' ).css( 'display', 'block' ); + }, + + upload_stitch: function( path ) { + + let _this = codiad.filemanager; + let form = new FormData(); + + form.append( 'path', path ); + + $.ajax({ + type: 'POST', + url: _this.controller + "?action=upload_stitch", + data: form, + processData: false, + contentType: false, + success: function( data ) { + + console.log( data ); + parent = path.split( '/' ); + + parent.pop(); + + console.log( path, parent.join( '/' ) ); + + _this.rescan( parent.join( '/' ) ); + }, + error: function( data ) { + + console.log( data ); + }, + }); + }, + + 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', async function( e ) { + + console.log( e ); + + let files = []; + let items = e.target.files; + + console.log( items, path ); + + codiad.modal.unload(); + + if( items.length ) { + + for( let i = items.length;i--; ) { + + let j = await _this.upload_load_files( items[i], path ); + files = files.concat( j ); + } + } + + console.log( files ); + + _this.create_upload_nodes( files ); + + for( let i = files.length;i--; ) { + + let status = await _this.upload( files[i] ); + //console.log( status ); + } + console.log( 'drop', files, items ); + }); + text.on( 'click', function( e ) { + + input.click(); + }); + + container.html( '' ); + container.append( text ); + container.append( input ); + }); + }, + + //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: 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: 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/components/filemanager/upload_scripts/jquery.fileupload.js b/components/filemanager/upload_scripts/jquery.fileupload.js deleted file mode 100755 index b2638ef..0000000 --- a/components/filemanager/upload_scripts/jquery.fileupload.js +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * jQuery File Upload Plugin 5.16.4 - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2010, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, Blob, FormData, location */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'jquery.ui.widget' - ], factory); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - // The FileReader API is not actually used, but works as feature detection, - // as e.g. Safari supports XHR file uploads via the FormData API, - // but not non-multipart XHR file uploads: - $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); - $.support.xhrFormDataFileUpload = !!window.FormData; - - // The fileupload widget listens for change events on file input fields defined - // via fileInput setting and paste or drop events of the given dropZone. - // In addition to the default jQuery Widget methods, the fileupload widget - // exposes the "add" and "send" methods, to add or directly send files using - // the fileupload API. - // By default, files added via file input selection, paste, drag & drop or - // "add" method are uploaded immediately, but it is possible to override - // the "add" callback option to queue file uploads. - $.widget('blueimp.fileupload', { - - options: { - // The namespace used for event handler binding on the dropZone and - // fileInput collections. - // If not set, the name of the widget ("fileupload") is used. - namespace: undefined, - // The drop target collection, by the default the complete document. - // Set to null or an empty collection to disable drag & drop support: - dropZone: $(document), - // The file input field collection, that is listened for change events. - // If undefined, it is set to the file input fields inside - // of the widget element on plugin initialization. - // Set to null or an empty collection to disable the change listener. - fileInput: undefined, - // By default, the file input field is replaced with a clone after - // each input field change event. This is required for iframe transport - // queues and allows change events to be fired for the same file - // selection, but can be disabled by setting the following option to false: - replaceFileInput: true, - // The parameter name for the file form data (the request argument name). - // If undefined or empty, the name property of the file input field is - // used, or "files[]" if the file input name property is also empty, - // can be a string or an array of strings: - paramName: undefined, - // By default, each file of a selection is uploaded using an individual - // request for XHR type uploads. Set to false to upload file - // selections in one request each: - singleFileUploads: true, - // To limit the number of files uploaded with one XHR request, - // set the following option to an integer greater than 0: - limitMultiFileUploads: undefined, - // Set the following option to true to issue all file upload requests - // in a sequential order: - sequentialUploads: false, - // To limit the number of concurrent uploads, - // set the following option to an integer greater than 0: - limitConcurrentUploads: undefined, - // Set the following option to true to force iframe transport uploads: - forceIframeTransport: false, - // Set the following option to the location of a redirect url on the - // origin server, for cross-domain iframe transport uploads: - redirect: undefined, - // The parameter name for the redirect url, sent as part of the form - // data and set to 'redirect' if this option is empty: - redirectParamName: undefined, - // Set the following option to the location of a postMessage window, - // to enable postMessage transport uploads: - postMessage: undefined, - // By default, XHR file uploads are sent as multipart/form-data. - // The iframe transport is always using multipart/form-data. - // Set to false to enable non-multipart XHR uploads: - multipart: true, - // To upload large files in smaller chunks, set the following option - // to a preferred maximum chunk size. If set to 0, null or undefined, - // or the browser does not support the required Blob API, files will - // be uploaded as a whole. - maxChunkSize: undefined, - // When a non-multipart upload or a chunked multipart upload has been - // aborted, this option can be used to resume the upload by setting - // it to the size of the already uploaded bytes. This option is most - // useful when modifying the options object inside of the "add" or - // "send" callbacks, as the options are cloned for each file upload. - uploadedBytes: undefined, - // By default, failed (abort or error) file uploads are removed from the - // global progress calculation. Set the following option to false to - // prevent recalculating the global progress data: - recalculateProgress: true, - // Interval in milliseconds to calculate and trigger progress events: - progressInterval: 100, - // Interval in milliseconds to calculate progress bitrate: - bitrateInterval: 500, - - // Additional form data to be sent along with the file uploads can be set - // using this option, which accepts an array of objects with name and - // value properties, a function returning such an array, a FormData - // object (for XHR file uploads), or a simple object. - // The form of the first fileInput is given as parameter to the function: - formData: function (form) { - return form.serializeArray(); - }, - - // The add callback is invoked as soon as files are added to the fileupload - // widget (via file input selection, drag & drop, paste or add API call). - // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uplaods, else - // once for each file selection. - // The upload starts when the submit method is invoked on the data parameter. - // The data object contains a files property holding the added files - // and allows to override plugin options as well as define ajax settings. - // Listeners for this callback can also be bound the following way: - // .bind('fileuploadadd', func); - // data.submit() returns a Promise object and allows to attach additional - // handlers using jQuery's Deferred callbacks: - // data.submit().done(func).fail(func).always(func); - add: function (e, data) { - data.submit(); - }, - - // Other callbacks: - // Callback for the submit event of each file upload: - // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); - // Callback for the start of each file upload request: - // send: function (e, data) {}, // .bind('fileuploadsend', func); - // Callback for successful uploads: - // done: function (e, data) {}, // .bind('fileuploaddone', func); - // Callback for failed (abort or error) uploads: - // fail: function (e, data) {}, // .bind('fileuploadfail', func); - // Callback for completed (success, abort or error) requests: - // always: function (e, data) {}, // .bind('fileuploadalways', func); - // Callback for upload progress events: - // progress: function (e, data) {}, // .bind('fileuploadprogress', func); - // Callback for global upload progress events: - // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); - // Callback for uploads start, equivalent to the global ajaxStart event: - // start: function (e) {}, // .bind('fileuploadstart', func); - // Callback for uploads stop, equivalent to the global ajaxStop event: - // stop: function (e) {}, // .bind('fileuploadstop', func); - // Callback for change events of the fileInput collection: - // change: function (e, data) {}, // .bind('fileuploadchange', func); - // Callback for paste events to the dropZone collection: - // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - // Callback for drop events of the dropZone collection: - // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - // Callback for dragover events of the dropZone collection: - // dragover: function (e) {}, // .bind('fileuploaddragover', func); - - // The plugin options are used as settings object for the ajax calls. - // The following are jQuery ajax settings required for the file uploads: - processData: false, - contentType: false, - cache: false - }, - - // A list of options that require a refresh after assigning a new value: - _refreshOptionsList: [ - 'namespace', - 'dropZone', - 'fileInput', - 'multipart', - 'forceIframeTransport' - ], - - _BitrateTimer: function () { - this.timestamp = +(new Date()); - this.loaded = 0; - this.bitrate = 0; - this.getBitrate = function (now, loaded, interval) { - var timeDiff = now - this.timestamp; - if (!this.bitrate || !interval || timeDiff > interval) { - this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8; - this.loaded = loaded; - this.timestamp = now; - } - return this.bitrate; - }; - }, - - _isXHRUpload: function (options) { - return !options.forceIframeTransport && - ((!options.multipart && $.support.xhrFileUpload) || - $.support.xhrFormDataFileUpload); - }, - - _getFormData: function (options) { - var formData; - if (typeof options.formData === 'function') { - return options.formData(options.form); - } - if ($.isArray(options.formData)) { - return options.formData; - } - if (options.formData) { - formData = []; - $.each(options.formData, function (name, value) { - formData.push({name: name, value: value}); - }); - return formData; - } - return []; - }, - - _getTotal: function (files) { - var total = 0; - $.each(files, function (index, file) { - total += file.size || 1; - }); - return total; - }, - - _onProgress: function (e, data) { - if (e.lengthComputable) { - var now = +(new Date()), - total, - loaded; - if (data._time && data.progressInterval && - (now - data._time < data.progressInterval) && - e.loaded !== e.total) { - return; - } - data._time = now; - total = data.total || this._getTotal(data.files); - loaded = parseInt( - e.loaded / e.total * (data.chunkSize || total), - 10 - ) + (data.uploadedBytes || 0); - this._loaded += loaded - (data.loaded || data.uploadedBytes || 0); - data.lengthComputable = true; - data.loaded = loaded; - data.total = total; - data.bitrate = data._bitrateTimer.getBitrate( - now, - loaded, - data.bitrateInterval - ); - // Trigger a custom progress event with a total data property set - // to the file size(s) of the current upload and a loaded data - // property calculated accordingly: - this._trigger('progress', e, data); - // Trigger a global progress event for all current file uploads, - // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, { - lengthComputable: true, - loaded: this._loaded, - total: this._total, - bitrate: this._bitrateTimer.getBitrate( - now, - this._loaded, - data.bitrateInterval - ) - }); - } - }, - - _initProgressListener: function (options) { - var that = this, - xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr(); - // Accesss to the native XHR object is required to add event listeners - // for the upload progress event: - if (xhr.upload) { - $(xhr.upload).bind('progress', function (e) { - var oe = e.originalEvent; - // Make sure the progress event properties get copied over: - e.lengthComputable = oe.lengthComputable; - e.loaded = oe.loaded; - e.total = oe.total; - that._onProgress(e, options); - }); - options.xhr = function () { - return xhr; - }; - } - }, - - _initXHRData: function (options) { - var formData, - file = options.files[0], - // Ignore non-multipart setting if not supported: - multipart = options.multipart || !$.support.xhrFileUpload, - paramName = options.paramName[0]; - if (!multipart || options.blob) { - // For non-multipart uploads and chunked uploads, - // file meta data is not part of the request body, - // so we transmit this data as part of the HTTP headers. - // For cross domain requests, these headers must be allowed - // via Access-Control-Allow-Headers or removed using - // the beforeSend callback: - options.headers = $.extend(options.headers, { - 'X-File-Name': file.name, - 'X-File-Type': file.type, - 'X-File-Size': file.size - }); - if (!options.blob) { - // Non-chunked non-multipart upload: - options.contentType = file.type; - options.data = file; - } else if (!multipart) { - // Chunked non-multipart upload: - options.contentType = 'application/octet-stream'; - options.data = options.blob; - } - } - if (multipart && $.support.xhrFormDataFileUpload) { - if (options.postMessage) { - // window.postMessage does not allow sending FormData - // objects, so we just add the File/Blob objects to - // the formData array and let the postMessage window - // create the FormData object out of this array: - formData = this._getFormData(options); - if (options.blob) { - formData.push({ - name: paramName, - value: options.blob - }); - } else { - $.each(options.files, function (index, file) { - formData.push({ - name: options.paramName[index] || paramName, - value: file - }); - }); - } - } else { - if (options.formData instanceof FormData) { - formData = options.formData; - } else { - formData = new FormData(); - $.each(this._getFormData(options), function (index, field) { - formData.append(field.name, field.value); - }); - } - if (options.blob) { - formData.append(paramName, options.blob, file.name); - } else { - $.each(options.files, function (index, file) { - // File objects are also Blob instances. - // This check allows the tests to run with - // dummy objects: - if (file instanceof Blob) { - formData.append( - options.paramName[index] || paramName, - file, - file.name - ); - } - }); - } - } - options.data = formData; - } - // Blob reference is not needed anymore, free memory: - options.blob = null; - }, - - _initIframeSettings: function (options) { - // Setting the dataType to iframe enables the iframe transport: - options.dataType = 'iframe ' + (options.dataType || ''); - // The iframe transport accepts a serialized array as form data: - options.formData = this._getFormData(options); - // Add redirect url to form data on cross-domain uploads: - if (options.redirect && $('').prop('href', options.url) - .prop('host') !== location.host) { - options.formData.push({ - name: options.redirectParamName || 'redirect', - value: options.redirect - }); - } - }, - - _initDataSettings: function (options) { - if (this._isXHRUpload(options)) { - if (!this._chunkedUpload(options, true)) { - if (!options.data) { - this._initXHRData(options); - } - this._initProgressListener(options); - } - if (options.postMessage) { - // Setting the dataType to postmessage enables the - // postMessage transport: - options.dataType = 'postmessage ' + (options.dataType || ''); - } - } else { - this._initIframeSettings(options, 'iframe'); - } - }, - - _getParamName: function (options) { - var fileInput = $(options.fileInput), - paramName = options.paramName; - if (!paramName) { - paramName = []; - fileInput.each(function () { - var input = $(this), - name = input.prop('name') || 'files[]', - i = (input.prop('files') || [1]).length; - while (i) { - paramName.push(name); - i -= 1; - } - }); - if (!paramName.length) { - paramName = [fileInput.prop('name') || 'files[]']; - } - } else if (!$.isArray(paramName)) { - paramName = [paramName]; - } - return paramName; - }, - - _initFormSettings: function (options) { - // Retrieve missing options from the input field and the - // associated form, if available: - if (!options.form || !options.form.length) { - options.form = $(options.fileInput.prop('form')); - } - options.paramName = this._getParamName(options); - if (!options.url) { - options.url = options.form.prop('action') || location.href; - } - // The HTTP request method must be "POST" or "PUT": - options.type = (options.type || options.form.prop('method') || '') - .toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT') { - options.type = 'POST'; - } - if (!options.formAcceptCharset) { - options.formAcceptCharset = options.form.attr('accept-charset'); - } - }, - - _getAJAXSettings: function (data) { - var options = $.extend({}, this.options, data); - this._initFormSettings(options); - this._initDataSettings(options); - return options; - }, - - // Maps jqXHR callbacks to the equivalent - // methods of the given Promise object: - _enhancePromise: function (promise) { - promise.success = promise.done; - promise.error = promise.fail; - promise.complete = promise.always; - return promise; - }, - - // Creates and returns a Promise object enhanced with - // the jqXHR methods abort, success, error and complete: - _getXHRPromise: function (resolveOrReject, context, args) { - var dfd = $.Deferred(), - promise = dfd.promise(); - context = context || this.options.context || promise; - if (resolveOrReject === true) { - dfd.resolveWith(context, args); - } else if (resolveOrReject === false) { - dfd.rejectWith(context, args); - } - promise.abort = dfd.promise; - return this._enhancePromise(promise); - }, - - // Uploads a file in multiple, sequential requests - // by splitting the file up in multiple blob chunks. - // If the second parameter is true, only tests if the file - // should be uploaded in chunks, but does not invoke any - // upload requests: - _chunkedUpload: function (options, testOnly) { - var that = this, - file = options.files[0], - fs = file.size, - ub = options.uploadedBytes = options.uploadedBytes || 0, - mcs = options.maxChunkSize || fs, - // Use the Blob methods with the slice implementation - // according to the W3C Blob API specification: - slice = file.webkitSlice || file.mozSlice || file.slice, - upload, - n, - jqXHR, - pipe; - if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || - options.data) { - return false; - } - if (testOnly) { - return true; - } - if (ub >= fs) { - file.error = 'uploadedBytes'; - return this._getXHRPromise( - false, - options.context, - [null, 'error', file.error] - ); - } - // n is the number of blobs to upload, - // calculated via filesize, uploaded bytes and max chunk size: - n = Math.ceil((fs - ub) / mcs); - // The chunk upload method accepting the chunk number as parameter: - upload = function (i) { - if (!i) { - return that._getXHRPromise(true, options.context); - } - // Upload the blobs in sequential order: - return upload(i -= 1).pipe(function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options); - o.blob = slice.call( - file, - ub + i * mcs, - ub + (i + 1) * mcs - ); - // Expose the chunk index: - o.chunkIndex = i; - // Expose the number of chunks: - o.chunksNumber = n; - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) - .done(function () { - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: o.chunkSize, - total: o.chunkSize - }), o); - } - options.uploadedBytes = o.uploadedBytes += - o.chunkSize; - }); - return jqXHR; - }); - }; - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe = upload(n); - pipe.abort = function () { - return jqXHR.abort(); - }; - return this._enhancePromise(pipe); - }, - - _beforeSend: function (e, data) { - if (this._active === 0) { - // the start callback is triggered when an upload starts - // and no other uploads are currently running, - // equivalent to the global ajaxStart event: - this._trigger('start'); - // Set timer for global bitrate progress calculation: - this._bitrateTimer = new this._BitrateTimer(); - } - this._active += 1; - // Initialize the global progress values: - this._loaded += data.uploadedBytes || 0; - this._total += this._getTotal(data.files); - }, - - _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options)) { - // Create a progress event for each iframe load: - this._onProgress($.Event('progress', { - lengthComputable: true, - loaded: 1, - total: 1 - }), options); - } - options.result = result; - options.textStatus = textStatus; - options.jqXHR = jqXHR; - this._trigger('done', null, options); - }, - - _onFail: function (jqXHR, textStatus, errorThrown, options) { - options.jqXHR = jqXHR; - options.textStatus = textStatus; - options.errorThrown = errorThrown; - this._trigger('fail', null, options); - if (options.recalculateProgress) { - // Remove the failed (error or abort) file upload from - // the global progress calculation: - this._loaded -= options.loaded || options.uploadedBytes || 0; - this._total -= options.total || this._getTotal(options.files); - } - }, - - _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { - this._active -= 1; - options.textStatus = textStatus; - if (jqXHRorError && jqXHRorError.always) { - options.jqXHR = jqXHRorError; - options.result = jqXHRorResult; - } else { - options.jqXHR = jqXHRorResult; - options.errorThrown = jqXHRorError; - } - this._trigger('always', null, options); - if (this._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - this._trigger('stop'); - // Reset the global progress values: - this._loaded = this._total = 0; - this._bitrateTimer = null; - } - }, - - _onSend: function (e, data) { - var that = this, - jqXHR, - slot, - pipe, - options = that._getAJAXSettings(data), - send = function (resolve, args) { - that._sending += 1; - // Set timer for bitrate progress calculation: - options._bitrateTimer = new that._BitrateTimer(); - jqXHR = jqXHR || ( - (resolve !== false && - that._trigger('send', e, options) !== false && - (that._chunkedUpload(options) || $.ajax(options))) || - that._getXHRPromise(false, options.context, args) - ).done(function (result, textStatus, jqXHR) { - that._onDone(result, textStatus, jqXHR, options); - }).fail(function (jqXHR, textStatus, errorThrown) { - that._onFail(jqXHR, textStatus, errorThrown, options); - }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._sending -= 1; - that._onAlways( - jqXHRorResult, - textStatus, - jqXHRorError, - options - ); - if (options.limitConcurrentUploads && - options.limitConcurrentUploads > that._sending) { - // Start the next queued upload, - // that has not been aborted: - var nextSlot = that._slots.shift(), - isPending; - while (nextSlot) { - // jQuery 1.6 doesn't provide .state(), - // while jQuery 1.8+ removed .isRejected(): - isPending = nextSlot.state ? - nextSlot.state() === 'pending' : - !nextSlot.isRejected(); - if (isPending) { - nextSlot.resolve(); - break; - } - nextSlot = that._slots.shift(); - } - } - }); - return jqXHR; - }; - this._beforeSend(e, options); - if (this.options.sequentialUploads || - (this.options.limitConcurrentUploads && - this.options.limitConcurrentUploads <= this._sending)) { - if (this.options.limitConcurrentUploads > 1) { - slot = $.Deferred(); - this._slots.push(slot); - pipe = slot.pipe(send); - } else { - pipe = (this._sequence = this._sequence.pipe(send, send)); - } - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe.abort = function () { - var args = [undefined, 'abort', 'abort']; - if (!jqXHR) { - if (slot) { - slot.rejectWith(pipe, args); - } - return send(false, args); - } - return jqXHR.abort(); - }; - return this._enhancePromise(pipe); - } - return send(); - }, - - _onAdd: function (e, data) { - var that = this, - result = true, - options = $.extend({}, this.options, data), - limit = options.limitMultiFileUploads, - paramName = this._getParamName(options), - paramNameSet, - paramNameSlice, - fileSet, - i; - if (!(options.singleFileUploads || limit) || - !this._isXHRUpload(options)) { - fileSet = [data.files]; - paramNameSet = [paramName]; - } else if (!options.singleFileUploads && limit) { - fileSet = []; - paramNameSet = []; - for (i = 0; i < data.files.length; i += limit) { - fileSet.push(data.files.slice(i, i + limit)); - paramNameSlice = paramName.slice(i, i + limit); - if (!paramNameSlice.length) { - paramNameSlice = paramName; - } - paramNameSet.push(paramNameSlice); - } - } else { - paramNameSet = paramName; - } - data.originalFiles = data.files; - $.each(fileSet || data.files, function (index, element) { - var newData = $.extend({}, data); - newData.files = fileSet ? element : [element]; - newData.paramName = paramNameSet[index]; - newData.submit = function () { - newData.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); - return this.jqXHR; - }; - return (result = that._trigger('add', e, newData)); - }); - return result; - }, - - _replaceFileInput: function (input) { - var inputClone = input.clone(true); - $('
    ').append(inputClone)[0].reset(); - // Detaching allows to insert the fileInput on another form - // without loosing the file input value: - input.after(inputClone).detach(); - // Avoid memory leaks with the detached file input: - $.cleanData(input.unbind('remove')); - // Replace the original file input element in the fileInput - // collection with the clone, which has been copied including - // event handlers: - this.options.fileInput = this.options.fileInput.map(function (i, el) { - if (el === input[0]) { - return inputClone[0]; - } - return el; - }); - // If the widget has been initialized on the file input itself, - // override this.element with the file input clone: - if (input[0] === this.element[0]) { - this.element = inputClone; - } - }, - - _handleFileTreeEntry: function (entry, path) { - var that = this, - dfd = $.Deferred(), - errorHandler = function () { - dfd.reject(); - }, - dirReader; - path = path || ''; - if (entry.isFile) { - entry.file(function (file) { - file.relativePath = path; - dfd.resolve(file); - }, errorHandler); - } else if (entry.isDirectory) { - dirReader = entry.createReader(); - dirReader.readEntries(function (entries) { - that._handleFileTreeEntries( - entries, - path + entry.name + '/' - ).done(function (files) { - dfd.resolve(files); - }).fail(errorHandler); - }, errorHandler); - } else { - errorHandler(); - } - return dfd.promise(); - }, - - _handleFileTreeEntries: function (entries, path) { - var that = this; - return $.when.apply( - $, - $.map(entries, function (entry) { - return that._handleFileTreeEntry(entry, path); - }) - ).pipe(function () { - return Array.prototype.concat.apply( - [], - arguments - ); - }); - }, - - _getDroppedFiles: function (dataTransfer) { - dataTransfer = dataTransfer || {}; - var items = dataTransfer.items; - if (items && items.length && (items[0].webkitGetAsEntry || - items[0].getAsEntry)) { - return this._handleFileTreeEntries( - $.map(items, function (item) { - if (item.webkitGetAsEntry) { - return item.webkitGetAsEntry(); - } - return item.getAsEntry(); - }) - ); - } - return $.Deferred().resolve( - $.makeArray(dataTransfer.files) - ).promise(); - }, - - _getFileInputFiles: function (fileInput) { - fileInput = $(fileInput); - var entries = fileInput.prop('webkitEntries') || - fileInput.prop('entries'), - files, - value; - if (entries && entries.length) { - return this._handleFileTreeEntries(entries); - } - files = $.makeArray(fileInput.prop('files')); - if (!files.length) { - value = fileInput.prop('value'); - if (!value) { - return $.Deferred().reject([]).promise(); - } - // If the files property is not available, the browser does not - // support the File API and we add a pseudo File object with - // the input value as name with path information removed: - files = [{name: value.replace(/^.*\\/, '')}]; - } - return $.Deferred().resolve(files).promise(); - }, - - _onChange: function (e) { - var that = e.data.fileupload, - data = { - fileInput: $(e.target), - form: $(e.target.form) - }; - that._getFileInputFiles(data.fileInput).always(function (files) { - data.files = files; - if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); - } - if (that._trigger('change', e, data) !== false) { - that._onAdd(e, data); - } - }); - }, - - _onPaste: function (e) { - var that = e.data.fileupload, - cbd = e.originalEvent.clipboardData, - items = (cbd && cbd.items) || [], - data = {files: []}; - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); - } - }); - if (that._trigger('paste', e, data) === false || - that._onAdd(e, data) === false) { - return false; - } - }, - - _onDrop: function (e) { - e.preventDefault(); - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, - data = {}; - that._getDroppedFiles(dataTransfer).always(function (files) { - data.files = files; - if (that._trigger('drop', e, data) !== false) { - that._onAdd(e, data); - } - }); - }, - - _onDragOver: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (that._trigger('dragover', e) === false) { - return false; - } - if (dataTransfer) { - dataTransfer.dropEffect = 'copy'; - } - e.preventDefault(); - }, - - _initEventHandlers: function () { - var ns = this.options.namespace; - if (this._isXHRUpload(this.options)) { - this.options.dropZone - .bind('dragover.' + ns, {fileupload: this}, this._onDragOver) - .bind('drop.' + ns, {fileupload: this}, this._onDrop) - .bind('paste.' + ns, {fileupload: this}, this._onPaste); - } - this.options.fileInput - .bind('change.' + ns, {fileupload: this}, this._onChange); - }, - - _destroyEventHandlers: function () { - var ns = this.options.namespace; - this.options.dropZone - .unbind('dragover.' + ns, this._onDragOver) - .unbind('drop.' + ns, this._onDrop) - .unbind('paste.' + ns, this._onPaste); - this.options.fileInput - .unbind('change.' + ns, this._onChange); - }, - - _setOption: function (key, value) { - var refresh = $.inArray(key, this._refreshOptionsList) !== -1; - if (refresh) { - this._destroyEventHandlers(); - } - $.Widget.prototype._setOption.call(this, key, value); - if (refresh) { - this._initSpecialOptions(); - this._initEventHandlers(); - } - }, - - _initSpecialOptions: function () { - var options = this.options; - if (options.fileInput === undefined) { - options.fileInput = this.element.is('input[type="file"]') ? - this.element : this.element.find('input[type="file"]'); - } else if (!(options.fileInput instanceof $)) { - options.fileInput = $(options.fileInput); - } - if (!(options.dropZone instanceof $)) { - options.dropZone = $(options.dropZone); - } - }, - - _create: function () { - var options = this.options; - // Initialize options set via HTML5 data-attributes: - $.extend(options, $(this.element[0].cloneNode(false)).data()); - options.namespace = options.namespace || this.widgetName; - this._initSpecialOptions(); - this._slots = []; - this._sequence = this._getXHRPromise(true); - this._sending = this._active = this._loaded = this._total = 0; - this._initEventHandlers(); - }, - - destroy: function () { - this._destroyEventHandlers(); - $.Widget.prototype.destroy.call(this); - }, - - enable: function () { - var wasDisabled = false; - if (this.options.disabled) { - wasDisabled = true; - } - $.Widget.prototype.enable.call(this); - if (wasDisabled) { - this._initEventHandlers(); - } - }, - - disable: function () { - if (!this.options.disabled) { - this._destroyEventHandlers(); - } - $.Widget.prototype.disable.call(this); - }, - - // This method is exposed to the widget API and allows adding files - // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: - // .fileupload('add', {files: filesList}); - add: function (data) { - var that = this; - if (!data || this.options.disabled) { - return; - } - if (data.fileInput && !data.files) { - this._getFileInputFiles(data.fileInput).always(function (files) { - data.files = files; - that._onAdd(null, data); - }); - } else { - data.files = $.makeArray(data.files); - this._onAdd(null, data); - } - }, - - // This method is exposed to the widget API and allows sending files - // using the fileupload API. The data parameter accepts an object which - // must have a files or fileInput property and can contain additional options: - // .fileupload('send', {files: filesList}); - // The method returns a Promise object for the file upload call. - send: function (data) { - if (data && !this.options.disabled) { - if (data.fileInput && !data.files) { - var that = this, - dfd = $.Deferred(), - promise = dfd.promise(), - jqXHR, - aborted; - promise.abort = function () { - aborted = true; - if (jqXHR) { - return jqXHR.abort(); - } - dfd.reject(null, 'abort', 'abort'); - return promise; - }; - this._getFileInputFiles(data.fileInput).always( - function (files) { - if (aborted) { - return; - } - data.files = files; - jqXHR = that._onSend(null, data).then( - function (result, textStatus, jqXHR) { - dfd.resolve(result, textStatus, jqXHR); - }, - function (jqXHR, textStatus, errorThrown) { - dfd.reject(jqXHR, textStatus, errorThrown); - } - ); - } - ); - return this._enhancePromise(promise); - } - data.files = $.makeArray(data.files); - if (data.files.length) { - return this._onSend(null, data); - } - } - return this._getXHRPromise(false, data && data.context); - } - - }); - -})); diff --git a/components/filemanager/upload_scripts/jquery.iframe-transport.js b/components/filemanager/upload_scripts/jquery.iframe-transport.js deleted file mode 100755 index 4749f46..0000000 --- a/components/filemanager/upload_scripts/jquery.iframe-transport.js +++ /dev/null @@ -1,172 +0,0 @@ -/* - * jQuery Iframe Transport Plugin 1.5 - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/*jslint unparam: true, nomen: true */ -/*global define, window, document */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define(['jquery'], factory); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - // Helper variable to create unique names for the transport iframes: - var counter = 0; - - // The iframe transport accepts three additional options: - // options.fileInput: a jQuery collection of file input fields - // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s), - // can be a string or an array of strings. - // options.formData: an array of objects with name and value properties, - // equivalent to the return data of .serializeArray(), e.g.: - // [{name: 'a', value: 1}, {name: 'b', value: 2}] - $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { - var form, - iframe; - return { - send: function (_, completeCallback) { - form = $('
    '); - form.attr('accept-charset', options.formAcceptCharset); - // javascript:false as initial iframe src - // prevents warning popups on HTTPS in IE6. - // IE versions below IE8 cannot set the name property of - // elements that have already been added to the DOM, - // so we set the name along with the iframe HTML markup: - iframe = $( - '' - ).bind('load', function () { - var fileInputClones, - paramNames = $.isArray(options.paramName) ? - options.paramName : [options.paramName]; - iframe - .unbind('load') - .bind('load', function () { - var response; - // Wrap in a try/catch block to catch exceptions thrown - // when trying to access cross-domain iframe contents: - try { - response = iframe.contents(); - // Google Chrome and Firefox do not throw an - // exception when calling iframe.contents() on - // cross-domain requests, so we unify the response: - if (!response.length || !response[0].firstChild) { - throw new Error(); - } - } catch (e) { - response = undefined; - } - // The complete callback returns the - // iframe content document as response object: - completeCallback( - 200, - 'success', - {'iframe': response} - ); - // Fix for IE endless progress bar activity bug - // (happens on form submits to iframe targets): - $('') - .appendTo(form); - form.remove(); - }); - form - .prop('target', iframe.prop('name')) - .prop('action', options.url) - .prop('method', options.type); - if (options.formData) { - $.each(options.formData, function (index, field) { - $('') - .prop('name', field.name) - .val(field.value) - .appendTo(form); - }); - } - if (options.fileInput && options.fileInput.length && - options.type === 'POST') { - fileInputClones = options.fileInput.clone(); - // Insert a clone for each file input field: - options.fileInput.after(function (index) { - return fileInputClones[index]; - }); - if (options.paramName) { - options.fileInput.each(function (index) { - $(this).prop( - 'name', - paramNames[index] || options.paramName - ); - }); - } - // Appending the file input fields to the hidden form - // removes them from their original location: - form - .append(options.fileInput) - .prop('enctype', 'multipart/form-data') - // enctype must be set as encoding for IE: - .prop('encoding', 'multipart/form-data'); - } - form.submit(); - // Insert the file input fields at their original location - // by replacing the clones with the originals: - if (fileInputClones && fileInputClones.length) { - options.fileInput.each(function (index, input) { - var clone = $(fileInputClones[index]); - $(input).prop('name', clone.prop('name')); - clone.replaceWith(input); - }); - } - }); - form.append(iframe).appendTo(document.body); - }, - abort: function () { - if (iframe) { - // javascript:false as iframe src aborts the request - // and prevents warning popups on HTTPS in IE6. - // concat is used to avoid the "Script URL" JSLint error: - iframe - .unbind('load') - .prop('src', 'javascript'.concat(':false;')); - } - if (form) { - form.remove(); - } - } - }; - } - }); - - // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: - $.ajaxSetup({ - converters: { - 'iframe text': function (iframe) { - return $(iframe[0].body).text(); - }, - 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); - }, - 'iframe html': function (iframe) { - return $(iframe[0].body).html(); - }, - 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); - } - } - }); - -})); diff --git a/components/filemanager/upload_scripts/jquery.ui.widget.js b/components/filemanager/upload_scripts/jquery.ui.widget.js deleted file mode 100755 index 39287bd..0000000 --- a/components/filemanager/upload_scripts/jquery.ui.widget.js +++ /dev/null @@ -1,282 +0,0 @@ -/* - * jQuery UI Widget 1.8.23+amd - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Widget - */ - -(function (factory) { - if (typeof define === "function" && define.amd) { - // Register as an anonymous AMD module: - define(["jquery"], factory); - } else { - // Browser globals: - factory(jQuery); - } -}(function( $, undefined ) { - -// jQuery 1.4+ -if ( $.cleanData ) { - var _cleanData = $.cleanData; - $.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); - }; -} else { - var _remove = $.fn.remove; - $.fn.remove = function( selector, keepData ) { - return this.each(function() { - if ( !keepData ) { - if ( !selector || $.filter( selector, [ this ] ).length ) { - $( "*", this ).add( [ this ] ).each(function() { - try { - $( this ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - }); - } - } - return _remove.call( $(this), selector, keepData ); - }); - }; -} - -$.widget = function( name, base, prototype ) { - var namespace = name.split( "." )[ 0 ], - fullName; - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName ] = function( elem ) { - return !!$.data( elem, name ); - }; - - $[ namespace ] = $[ namespace ] || {}; - $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - - var basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from -// $.each( basePrototype, function( key, val ) { -// if ( $.isPlainObject(val) ) { -// basePrototype[ key ] = $.extend( {}, val ); -// } -// }); - basePrototype.options = $.extend( true, {}, basePrototype.options ); - $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { - namespace: namespace, - widgetName: name, - widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, - widgetBaseClass: fullName - }, prototype ); - - $.widget.bridge( name, $[ namespace ][ name ] ); -}; - -$.widget.bridge = function( name, object ) { - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = Array.prototype.slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.extend.apply( null, [ true, options ].concat(args) ) : - options; - - // prevent calls to internal methods - if ( isMethodCall && options.charAt( 0 ) === "_" ) { - return returnValue; - } - - if ( isMethodCall ) { - this.each(function() { - var instance = $.data( this, name ), - methodValue = instance && $.isFunction( instance[options] ) ? - instance[ options ].apply( instance, args ) : - instance; - // TODO: add this back in 1.9 and use $.error() (see #5972) -// if ( !instance ) { -// throw "cannot call methods on " + name + " prior to initialization; " + -// "attempted to call method '" + options + "'"; -// } -// if ( !$.isFunction( instance[options] ) ) { -// throw "no such method '" + options + "' for " + name + " widget instance"; -// } -// var methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, name ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, name, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } -}; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - options: { - disabled: false - }, - _createWidget: function( options, element ) { - // $.widget.bridge stores the plugin instance, but we do it anyway - // so that it's stored even before the _create function runs - $.data( element, this.widgetName, this ); - this.element = $( element ); - this.options = $.extend( true, {}, - this.options, - this._getCreateOptions(), - options ); - - var self = this; - this.element.bind( "remove." + this.widgetName, function() { - self.destroy(); - }); - - this._create(); - this._trigger( "create" ); - this._init(); - }, - _getCreateOptions: function() { - return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; - }, - _create: function() {}, - _init: function() {}, - - destroy: function() { - this.element - .unbind( "." + this.widgetName ) - .removeData( this.widgetName ); - this.widget() - .unbind( "." + this.widgetName ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetBaseClass + "-disabled " + - "ui-state-disabled" ); - }, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.extend( {}, this.options ); - } - - if (typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } - options = {}; - options[ key ] = value; - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var self = this; - $.each( options, function( key, value ) { - self._setOption( key, value ); - }); - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - [ value ? "addClass" : "removeClass"]( - this.widgetBaseClass + "-disabled" + " " + - "ui-state-disabled" ) - .attr( "aria-disabled", value ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - - return !( $.isFunction(callback) && - callback.call( this.element[0], event, data ) === false || - event.isDefaultPrevented() ); - } -}; - -})); diff --git a/components/install/install.php b/components/install/install.php index 4d9e1ee..f5bf2b2 100644 --- a/components/install/install.php +++ b/components/install/install.php @@ -5,6 +5,8 @@ require_once( __DIR__ . "/../settings/class.settings.php" ); class Install { + const PATH_REGEX = '/[^\w\-\._@]/'; + public $active = ""; public $config = ""; public $db_types = array(); @@ -84,7 +86,7 @@ class Install { function clean_username( $username ) { - return strtolower( preg_replace( '/[^\w\-\._@]/', '-', $username ) ); + return strtolower( preg_replace( self::PATH_REGEX, '-', $username ) ); } function create_config() { @@ -148,20 +150,22 @@ define("WSURL", BASE_URL . "/workspace"); // Marketplace //define("MARKETURL", "http://market.codiad.com/json"); '; - $this->save_file( $this->config, $config_data ); - echo( "success" ); + return file_put_contents( $this->config, $config_data ); } function create_project() { $project_path = $this->project_path; + $connection = $this->sql->connect(); if ( ! $this->is_abs_path( $project_path ) ) { - $project_path = preg_replace( '/[^\w-._@]/', '-', $project_path ); + $project_path = preg_replace( self::PATH_REGEX, '-', $project_path ); + $project_path = $this->username . "/" . $project_path; + if( ! is_dir( $this->workspace . "/" . $project_path ) ) { - mkdir( $this->workspace . "/" . $project_path ); + mkdir( $this->workspace . "/" . $project_path, 0755, true ); } } else { @@ -184,57 +188,52 @@ define("WSURL", BASE_URL . "/workspace"); } } + $query = "DELETE FROM projects WHERE path = ?;"; + $bind_variables = array( + $project_path + ); + $statement = $connection->prepare( $query ); + $statement->execute( $bind_variables ); + + $query = "INSERT INTO projects(name, path, owner) VALUES (?,?,( SELECT id FROM users WHERE username = ? LIMIT 1 ));"; $bind_variables = array( $this->project_name, $project_path, $this->username ); - $query = "INSERT INTO projects(name, path, owner) VALUES (?,?,?);"; - $connection = $this->sql->connect(); $statement = $connection->prepare( $query ); $statement->execute( $bind_variables ); - $error = $statement->errorInfo(); - - if( ! $error[0] == "00000" ) { - - die( '{"message":"Could not create project in database.","error":"' . addslashes(json_encode( $error )) .'"}' ); - } } function create_tables() { $result = $this->sql->create_default_tables(); - if ( ! $result === true ) { + if ( ! $result["create_tables"] === true ) { - die( '{"message":"Could not tables in database.","error":"' . json_encode( $result ) .'"}' ); + exit( json_encode( $result ) ); } } function create_user() { $bind_variables = array( - "", - "", $this->username, $this->password, - "", $this->project_path, - "admin", - "", - "" + Permissions::LEVELS["admin"] ); - $query = "INSERT INTO users(first_name, last_name, username, password, email, project, access, groups, token) VALUES (?,?,?,?,?,?,?,?,?)"; - $connection = $this->sql->connect(); - $statement = $connection->prepare( $query ); - $statement->execute( $bind_variables ); - $error = $statement->errorInfo(); + $query = "INSERT INTO users( username, password, project, access ) VALUES ( ?,?,( SELECT id FROM projects WHERE path = ? LIMIT 1 ),? )"; - if( ! $error[0] == "00000" ) { + try { - die( '{"message":"Could not create user in database.","error":"' . addslashes(json_encode( $error )) .'"}' ); + $connection = $this->sql->connect(); + $statement = $connection->prepare( $query ); + $statement->execute( $bind_variables ); + } catch( exception $e ) { + + exit( "Error could not create user: " . $e->getMessage() ); } - $this->set_default_options(); } @@ -269,10 +268,11 @@ define("WSURL", BASE_URL . "/workspace"); $connection = $this->sql->connect(); $this->create_tables(); - $this->create_project(); $this->create_user(); + $this->create_project(); //exit( "stop" ); $this->create_config(); + return "success"; } function JSEND( $message, $error=null ) { @@ -288,18 +288,14 @@ define("WSURL", BASE_URL . "/workspace"); exit( json_encode( $message ) ); } - function save_file( $file, $data ) { - - $write = fopen( $file, 'w' ) or die( '{"message": "can\'t open file"}' ); - fwrite( $write, $data ); - fclose( $write ); - } - public function set_default_options() { foreach( Settings::DEFAULT_OPTIONS as $id => $option ) { - $query = "INSERT INTO user_options ( name, username, value ) VALUES ( ?, ?, ? );"; + $query = array( + "*" => "INSERT INTO user_options ( name, user, value ) VALUES ( ?, ( SELECT id FROM users WHERE username = ? ), ? );", + "pgsql" => 'INSERT INTO user_options ( name, "user", value ) VALUES ( ?, ( SELECT id FROM users WHERE username = ? ), ? );', + ); $bind_variables = array( $option["name"], $this->username, @@ -309,7 +305,10 @@ define("WSURL", BASE_URL . "/workspace"); if( $result == 0 ) { - $query = "UPDATE user_options SET value=? WHERE name=? AND username=?;"; + $query = array( + "*" => "UPDATE user_options SET value=? WHERE name=? AND user=( SELECT id FROM users WHERE username = ? );", + "pgsql" => 'UPDATE user_options SET value=? WHERE name=? AND "user"=( SELECT id FROM users WHERE username = ? );', + ); $bind_variables = array( $option["value"], $option["name"], diff --git a/components/install/view.php b/components/install/view.php index 0796f85..aca5452 100755 --- a/components/install/view.php +++ b/components/install/view.php @@ -442,12 +442,12 @@ if(!password_match){ alert('The passwords entered do not match'); } if(!empty_fields && password_match && check_path){ $.post('components/install/install.php',$('#install').serialize(),function( data ) { -if( data == 'success' ){ +console.log( data ); + +if( data === "success" || data == "" ){ window.location.reload(); } else { -data = JSON.parse( data ); -console.log( data.error ); -alert( "An Error Occurred\n" + data.message ); +alert( "An Error Occurred. Please check the console for more information.\n" ); } }); } diff --git a/components/keybindings/init.js b/components/keybindings/init.js index 6e53bfd..5c0e20a 100755 --- a/components/keybindings/init.js +++ b/components/keybindings/init.js @@ -39,6 +39,63 @@ codiad.keybindings = { + bindings: [ + { + name: 'Find', + bindKey: { + win: 'Ctrl-F', + mac: 'Command-F' + }, + exec: function( e ) { + + codiad.editor.openSearch( 'find' ); + } + }, + { + name: 'Goto Line', + bindKey: { + win: 'Ctrl-L', + mac: 'Command-L' + }, + exec: function( e ) { + + codiad.editor.open_goto(); + } + }, + { + name: 'Move Down', + bindKey: { + win: 'Ctrl-down', + mac: 'Command-up' + }, + exec: function( e ) { + codiad.active.move( 'down' ); + } + }, + { + name: 'Move Up', + bindKey: { + win: 'Ctrl-up', + mac: 'Command-up' + }, + exec: function( e ) { + + codiad.active.move( 'up' ); + } + }, + { + name: 'Replace', + bindKey: { + win: 'Ctrl-R', + mac: 'Command-R' + }, + exec: function( e ) { + + codiad.editor.openSearch( 'replace' ); + } + } + ], + init: function() { // Active List Next [CTRL+DOWN] ////////////////////////////// @@ -74,6 +131,12 @@ codiad.editor.openSearch( 'find' ); }); + // Find [CTRL+L] ///////////////////////////////////////////// + $.ctrl( '76', function() { + + codiad.editor.open_goto(); + }); + // Open in browser [CTRL+O] ////////////////////////////////// $.ctrl( '79', function() { diff --git a/components/market/class.market.php b/components/market/class.market.php index 4200b03..f80d1cd 100755 --- a/components/market/class.market.php +++ b/components/market/class.market.php @@ -224,7 +224,7 @@ class Market extends Common } } else { $reponame = explode('/', $repo); - $tmp = file_get_contents($this->url.'/?t='.rtrim($type, "s").'&i='.str_replace("-master", "", $reponame[sizeof($repo)-1])); + $tmp = file_get_contents($this->url.'/?t='.rtrim($type, "s").'&i='.str_replace("-master", "", $reponame[@sizeof($repo)-1])); } if (file_put_contents(BASE_PATH.'/'.$type.'/'.$name.'.zip', fopen($repo.'/archive/master.zip', 'r'))) { $zip = new ZipArchive; diff --git a/components/permissions/class.permissions.php b/components/permissions/class.permissions.php new file mode 100644 index 0000000..5f27acd --- /dev/null +++ b/components/permissions/class.permissions.php @@ -0,0 +1,168 @@ + 0, + "read" => 1, + "write" => 2, + "create" => 4, + "delete" => 8, + "manager" => 16, + "owner" => 32, + "admin" => 64, + ); + + const SYSTEM_LEVELS = array( + + "user" => 32, + "admin" => 64, + ); + + function __construct() { + + + } + + public static function check_access( $level, $user_level ) { + + if( ! is_integer( $level ) ) { + + if( in_array( $level, array_keys( self::LEVELS ) ) ) { + + $level = self::LEVELS[$level]; + } else { + + exit( formatJSEND( "error", "Access Level does not exist." ) ); + } + } else { + + if( ! in_array( $level, self::LEVELS ) ) { + + exit( formatJSEND( "error", "Access Level does not exist." ) ); + } + } + + return ( $user_level >= $level ); + } + + public static function check_path( $level, $path ) { + + $user_level = self::get_access( $path ); + return self::check_access( $level, $user_level ); + } + + public static function get_access( $path ) { + + global $sql; + $full_path = Common::isAbsPath( $path ) ? $path : WORKSPACE . "/{$path}"; + $access = 0; + //$query = "SELECT id, path, owner FROM projects WHERE path LIKE ?;"; + //$bind_variables = array( "{$path}%" ); + $query = "SELECT id, path, owner FROM projects;"; + $bind_variables = array(); + $projects = $sql->query( $query, $bind_variables, array() ); + + if( ! empty( $projects ) ) { + + foreach( $projects as $row => $data ) { + + $full_project_path = Common::isAbsPath( $data["path"] ) ? $data["path"] : WORKSPACE . "/{$data["path"]}"; + $path_postition = strpos( $full_path, $full_project_path ); + + if( $path_postition === false ) { + + continue; + } + + if( $data["owner"] == -1 ) { + + $access = self::LEVELS["owner"]; + } elseif( $data["owner"] == $_SESSION["user_id"] ) { + + $access = self::LEVELS["owner"]; + } else { + + $user = $sql->query( array( + "*" => "SELECT * FROM access WHERE project = ? AND user = ? LIMIT 1", + "pgsql" => 'SELECT * FROM access WHERE project = ? AND "user" = ? LIMIT 1', + ), array( $data["id"], $_SESSION["user_id"] ), array(), "fetch" ); + + if( ! empty( $user ) ) { + + $access = $user["level"]; + } + } + + //echo var_dump( $full_path, $full_project_path, $path_postition, $user["level"], $data["owner"], $_SESSION["user"] ); + if( $access > 0 ) { + + break; + } + } + } + return $access; + } + + public static function get_level( $i ) { + + $level = 0; + if( is_integer( $i ) ) { + + $level = array_search( $i, self::LEVELS ); + } else { + + if( in_array( $i, array_keys( self::LEVELS ) ) ) { + + $level = self::LEVELS[$i]; + } else { + + exit( formatJSEND( "error", "Access Level does not exist." ) ); + } + } + + return $level; + } + + public static function has_owner( $path ) { + + return self::check_path( "owner", $path ); + } + + public static function has_manager( $path ) { + + return self::check_path( "manager", $path ); + } + + public static function has_delete( $path ) { + + return self::check_path( "delete", $path ); + } + + public static function has_create( $path ) { + + return self::check_path( "create", $path ); + } + + public static function has_write( $path ) { + + return self::check_path( "write", $path ); + } + + public static function has_read( $path ) { + + return self::check_path( "read", $path ); + } +} + +?> \ No newline at end of file diff --git a/components/project/class.project.php b/components/project/class.project.php index 1497fa8..bc2021a 100755 --- a/components/project/class.project.php +++ b/components/project/class.project.php @@ -7,6 +7,7 @@ */ require_once( '../../common.php' ); +require_once( '../filemanager/class.filemanager.php' ); class Project extends Common { @@ -14,15 +15,15 @@ class Project extends Common { // PROPERTIES ////////////////////////////////////////////////////////////////// - public $name = ''; - public $path = ''; - public $gitrepo = false; - public $gitbranch = ''; - public $projects = array(); - public $no_return = false; - public $assigned = false; + public $access = Permissions::LEVELS["read"]; + public $name = ''; + public $path = ''; + public $gitrepo = false; + public $gitbranch = ''; + public $projects = array(); + public $no_return = false; + public $assigned = false; public $command_exec = ''; - public $public_project = false; public $user = ''; ////////////////////////////////////////////////////////////////// @@ -43,67 +44,99 @@ class Project extends Common { // NEW METHODS ////////////////////////////////////////////////////////////////// - public function add_project( $project_name, $project_path, $owner = null ) { + public function add_project( $project_name, $project_path, $owner ) { global $sql; - if( $this->public_project ) { - - $owner = 'nobody'; - } else { - - $owner = $_SESSION["user"]; - } - $query = "INSERT INTO projects( name, path, owner ) VALUES ( ?, ?, ? );"; $bind_variables = array( $project_name, $project_path, $owner ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - - if( ! ( $return > 0 ) ) { - - exit( formatJSEND( "error", "Error creating project $project_name" ) ); - } + return $return; } - public function add_user() { + public function add_user( $path, $user_id, $access ) { global $sql; - $query = "SELECT access FROM projects WHERE path=? AND owner=?"; - $bind_variables = array( $this->path, $_SESSION["user"] ); - $result = $sql->query( $query, $bind_variables, array() )[0]; - - if( ! empty( $result ) ) { + $return = array( + "status" => null, + "message" => null, + ); + $query = "SELECT * FROM projects WHERE path=? AND owner=? LIMIT 1"; + $bind_variables = array( $path, $_SESSION["user_id"] ); + $project = $sql->query( $query, $bind_variables, array(), "fetch" ); + + if( empty( $project ) ) { - $access = json_decode( $result["access"] ); + $return["status"] = "error"; + $return["message"] = "Error fetching projects."; + } else { - if( is_array( $access ) ) { + $user = $sql->query( array( + "*" => "SELECT * FROM access WHERE project = ? AND user = ? LIMIT 1", + "pgsql" => 'SELECT * FROM access WHERE project = ? AND "user" = ? LIMIT 1', + ), array( $project["id"], $user_id ), array(), "fetch" ); + + if( ! empty( $user ) ) { - if( ! in_array( $this->user, $access ) ) { + $query = array( + "*" => "UPDATE access SET level=? WHERE project=? AND user=?;", + "pgsql" => 'UPDATE access SET level=? WHERE project=? AND "user"=?;', + ); + $bind_variables = array( $access, $project["id"], $user_id ); + $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); + + if( $result > 0 ) { - array_push( $access, $this->user ); + $return["status"] = "success"; + $return["message"] = "Successfully updated access."; + } else { + + $return["status"] = "error"; + $return["message"] = "Error setting access for project."; } } else { - $access = array( - $this->user + $query = array( + "*" => "INSERT INTO access ( project, user, level ) VALUES ( ?,?,? );", + "pgsql" => 'INSERT INTO access ( project, "user", level ) VALUES ( ?,?,? );', ); - } - - $access = json_encode( $access ); - $query = "UPDATE projects SET access=? WHERE path=? AND owner=?;"; - $bind_variables = array( $access, $this->path, $_SESSION["user"] ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - - if( $result > 0 ) { - - echo( formatJSEND( "success", "Successfully added {$this->user}." ) ); - } else { + $bind_variables = array( $project["id"], $user_id, $access ); + $result = $sql->query( $query, $bind_variables, 0, "rowCount", "exception" ); - echo formatJSEND( "error", "Error setting access for project." ); + if( $result > 0 ) { + + $return["status"] = "success"; + $return["message"] = "Successfully updated access."; + } else { + + $return["status"] = "error"; + $return["message"] = "Error setting access for project."; + } } - } else { - - echo formatJSEND( "error", "Error fetching projects." ); } + return $return; + } + + public function check_duplicate( $full_path ) { + + global $sql; + $pass = true; + $query = "SELECT id, path, owner FROM projects;"; + $result = $sql->query( $query, array(), array(), "fetchAll" ); + + if( ! empty( $result ) ) { + + foreach( $result as $row => $project ) { + + $full_project_path = Common::isAbsPath( $project["path"] ) ? $project["path"] : WORKSPACE . "/{$project["path"]}"; + + if( ! ( strpos( $full_path, $full_project_path ) === FALSE ) ) { + + $pass = false; + break; + } + } + } + return $pass; } public function check_owner( $path = null, $exclude_public = false ) { @@ -115,7 +148,7 @@ class Project extends Common { global $sql; $query = "SELECT owner FROM projects WHERE path=?"; $bind_variables = array( $path ); - $result = $sql->query( $query, $bind_variables, array() )[0]; + $result = $sql->query( $query, $bind_variables, array(), "fetch" ); $return = false; if( ! empty( $result ) ) { @@ -123,13 +156,13 @@ class Project extends Common { $owner = $result["owner"]; if( $exclude_public ) { - if( $owner == $_SESSION["user"] ) { + if( $owner == $_SESSION["user_id"] ) { $return = true; } } else { - if( $owner == $_SESSION["user"] || $owner == 'nobody' ) { + if( $owner == $_SESSION["user_id"] || $owner == 'nobody' ) { $return = true; } @@ -138,25 +171,12 @@ class Project extends Common { return( $return ); } - public function get_access( $path = null ) { + public function get_access( $project_id = null ) { - if( $path === null ) { - - $path = $this->path; - } global $sql; - $query = "SELECT access FROM projects WHERE path=?"; - $bind_variables = array( $path ); - $return = $sql->query( $query, $bind_variables, array() )[0]; - - if( ! empty( $return ) ) { - - $return = $return["access"]; - } else { - - $return = formatJSEND( "error", "Error fetching project info." ); - } - + $query = "SELECT * FROM access WHERE project=?"; + $bind_variables = array( $project_id ); + $return = $sql->query( $query, $bind_variables, array() ); return( $return ); } @@ -169,7 +189,7 @@ class Project extends Common { global $sql; $query = "SELECT owner FROM projects WHERE path=?"; $bind_variables = array( $path ); - $return = $sql->query( $query, $bind_variables, array() )[0]; + $return = $sql->query( $query, $bind_variables, array(), "fetch" ); if( ! empty( $return ) ) { @@ -188,90 +208,120 @@ class Project extends Common { $project = $this->path; } + global $sql; - $query = "SELECT * FROM projects WHERE path=? AND ( owner=? OR owner='nobody' ) ORDER BY name;"; - $bind_variables = array( $project, $_SESSION["user"] ); - $return = $sql->query( $query, $bind_variables, array() )[0]; + $query = array( + "*" => " + SELECT * FROM projects + WHERE path = ? + AND ( + owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE user = ? ) + ) ORDER BY name; + ", + "pgsql" => ' + SELECT * FROM projects + WHERE path = ? + AND ( + owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE "user" = ? ) + ) ORDER BY name; + ', + ); + $bind_variables = array( $project, $_SESSION["user_id"], $_SESSION["user_id"] ); + //$query = "SELECT * FROM projects WHERE path=? AND ( owner=? OR owner='nobody' ) ORDER BY name;"; + //$bind_variables = array( $project, $_SESSION["user"] ); + $return = $sql->query( $query, $bind_variables, array(), "fetch" ); if( ! empty( $return ) ) { } else { - $return = formatJSEND( "error", "Error fetching projects." ); + $return = formatJSEND( "error", "No projects found." ); } return( $return ); } + public function get_all_projects() { + + if( is_admin() ) { + + global $sql; + $query = "SELECT * FROM projects"; + $bind_variables = array(); + $return = $sql->query( $query, $bind_variables, array() ); + + if( empty( $return ) ) { + + $return = formatJSEND( "error", "No projects found." ); + } + } else { + + $return = formatJSEND( "error", "Only admins are allowed to view all projects." ); + } + return( $return ); + } + public function get_projects() { global $sql; - $query = "SELECT * FROM projects WHERE owner=? OR owner='nobody' OR access LIKE ? ORDER BY name;"; - $bind_variables = array( $_SESSION["user"], '%"' . $_SESSION["user"] . '"%' ); + $query = array( + "*" => "SELECT * FROM projects + WHERE owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE user = ? ); + ", + "pgsql" => 'SELECT * FROM projects + WHERE owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE "user" = ? ); + ', + ); + $bind_variables = array( $_SESSION["user_id"], $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, array() ); - - if( empty( $return ) ) { - - $return = formatJSEND( "error", "Error fetching projects." ); - } - return( $return ); } - public function remove_user() { + public function remove_user( $user_id ) { global $sql; - $query = "SELECT access FROM projects WHERE path=? AND owner=?"; - $bind_variables = array( $this->path, $_SESSION["user"] ); - $result = $sql->query( $query, $bind_variables, array() )[0]; - - if( ! empty( $result ) ) { + + if( $user_id === false ) { - $access = json_decode( $result["access"] ); + return formatJSEND( "error", "Error fetching user information." ); + } + + $query = array( + "*" => "DELETE FROM access WHERE project=? AND user=?;", + "pgsql" => 'DELETE FROM access WHERE project=? AND "user"=?;', + ); + $bind_variables = array( $this->project_id, $user_id ); + $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + + if( $return > 0 ) { - if( is_array( $access ) ) { - - $key = array_search( $this->user, $access ); - - if ( $key !== false ) { - - unset( $access[$key] ); - } else { - - echo( formatJSEND( "error", "{$this->user} is not in the access list." ) ); - } - } - - $access = json_encode( $access ); - $query = "UPDATE projects SET access=? WHERE path=? AND owner=?;"; - $bind_variables = array( $access, $this->path, $_SESSION["user"] ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - - if( $return > 0 ) { - - echo( formatJSEND( "success", "Successfully removed {$this->user}." ) ); - } else { - - echo formatJSEND( "error", "Error setting access for project." ); - } + echo( formatJSEND( "success", "Successfully removed {$this->user}." ) ); } else { - echo formatJSEND( "error", "Error fetching projects." ); + echo( formatJSEND( "error", "{$this->user} is not in the access list." ) ); } } public function rename_project( $old_name, $new_name, $path ) { global $sql; - $query = "SELECT * FROM projects WHERE name=? AND path=? AND ( owner=? OR owner='nobody' );"; - $bind_variables = array( $old_name, $path, $_SESSION["user"] ); + $query = "SELECT * FROM projects WHERE name=? AND path=? AND ( owner=? OR owner=-1 );"; + $bind_variables = array( $old_name, $path, $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, array() ); $pass = false; if( ! empty( $return ) ) { - $query = "UPDATE projects SET name=? WHERE name=? AND path=? AND ( owner=? OR owner='nobody' );"; - $bind_variables = array( $new_name, $old_name, $path, $_SESSION["user"] ); + $query = "UPDATE projects SET name=? WHERE name=? AND path=? AND ( owner=? OR owner=-1 );"; + $bind_variables = array( $new_name, $old_name, $path, $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, 0, "rowCount"); if( $return > 0 ) { @@ -302,16 +352,17 @@ class Project extends Common { public function GetFirst() { + if( ! is_array( $this->projects ) || empty( $this->projects ) ) { + + return null; + } + $this->name = $this->projects[0]['name']; $this->path = $this->projects[0]['path']; // Set Sessions $_SESSION['project'] = $this->path; - - if ( ! $this->no_return ) { - - echo formatJSEND( "success", $this->projects[0] ); - } + return $this->projects[0]; } ////////////////////////////////////////////////////////////////// @@ -337,17 +388,35 @@ class Project extends Common { public function Open() { global $sql; - $query = "SELECT * FROM projects WHERE path=? AND ( owner=? OR owner='nobody' OR access LIKE ? );"; - $bind_variables = array( $this->path, $_SESSION["user"], '%"' . $_SESSION["user"] . '"%' ); - $return = $sql->query( $query, $bind_variables, array() )[0]; + $query = array( + "*" => "SELECT * FROM projects + WHERE path = ? + AND ( + owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE user = ? ) + ) ORDER BY name LIMIT 1; + ", + "pgsql" => 'SELECT * FROM projects + WHERE path = ? + AND ( + owner=? + OR owner=-1 + OR id IN ( SELECT project FROM access WHERE "user" = ? ) + ) ORDER BY name LIMIT 1; + ', + ); + $bind_variables = array( $this->path, $_SESSION["user_id"], $_SESSION["user_id"] ); + $return = $sql->query( $query, $bind_variables, array(), "fetch" ); if( ! empty( $return ) ) { $query = "UPDATE users SET project=? WHERE username=?;"; - $bind_variables = array( $this->path, $_SESSION["user"] ); + $bind_variables = array( $return["id"], $_SESSION["user"] ); $sql->query( $query, $bind_variables, 0, "rowCount" ); $this->name = $return['name']; $_SESSION['project'] = $return['path']; + $_SESSION['project_id'] = $return['id']; echo formatJSEND( "success", array( "name" => $this->name, "path" => $this->path ) ); } else { @@ -360,102 +429,127 @@ class Project extends Common { // Create ////////////////////////////////////////////////////////////////// - public function Create() { + public function Create( $path, $name, $public ) { + + $return = array( + "status" => null, + "message" => null, + ); + + if( $public === true ) { - if ( $this->name != '' && $this->path != '' ) { + $owner = -1; + } else { - $this->path = $this->cleanPath(); - $this->name = htmlspecialchars( $this->name ); - if ( ! $this->isAbsPath( $this->path ) ) { + $owner = $_SESSION["user_id"]; + } + + if ( $name != '' && $path != '' ) { + + $path = $this->clean_path( $path ); + $name = htmlspecialchars( $name ); + if ( ! $this->isAbsPath( $path ) ) { - $this->path = $this->SanitizePath(); + $path = $this->sanitize_path( $path ); } - if ( $this->path != '' ) { + if ( $path != '' ) { - if( ! $this->public_project && ! $this->isAbsPath( $this->path ) ) { + $user_path = WORKSPACE . '/' . preg_replace( Filemanager::PATH_REGEX, '', strtolower( $_SESSION["user"] ) ); + + if( ! $this->isAbsPath( $path ) ) { - $user_path = WORKSPACE . '/' . preg_replace( '/[^\w-]/', '', strtolower( $_SESSION["user"] ) ); + $path = $_SESSION["user"] . '/' . $path; + } + + $pass = $this->check_duplicate( $path ); + if ( $pass ) { if( ! is_dir( $user_path ) ) { mkdir( $user_path, 0755, true ); } - $this->path = $_SESSION["user"] . '/' . $this->path; - } - - $pass = $this->checkDuplicate(); - if ( $pass ) { - - if ( ! $this->isAbsPath( $this->path ) ) { + if ( ! $this->isAbsPath( $path ) ) { - mkdir( WORKSPACE . '/' . $this->path ); + if( ! is_dir( WORKSPACE . '/' . $path ) ) { + + mkdir( WORKSPACE . '/' . $path, 0755, true ); + } } else { - if( ! is_admin() ) { + if( is_admin() ) { - die( formatJSEND( "error", "Absolute Paths are only allowed for admins" ) ); - } - - if ( defined( 'WHITEPATHS' ) ) { - - $allowed = false; - foreach ( explode( ",", WHITEPATHS ) as $whitepath ) { + if ( defined( 'WHITEPATHS' ) ) { - if ( strpos( $this->path, $whitepath ) === 0 ) { + $allowed = false; + foreach ( explode( ",", WHITEPATHS ) as $whitepath ) { - $allowed = true; + if ( strpos( $path, $whitepath ) === 0 ) { + + $allowed = true; + } + } + if ( ! $allowed ) { + + $return["status"] = "error"; + $return["message"] = "Absolute Path Only Allowed for " . WHITEPATHS; } } - if ( ! $allowed) { + if ( ! file_exists( $path ) ) { - die( formatJSEND( "error", "Absolute Path Only Allowed for " . WHITEPATHS ) ); - } - } - if ( ! file_exists( $this->path ) ) { - - if ( ! mkdir( $this->path . '/', 0755, true ) ) { + if ( ! mkdir( $path . '/', 0755, true ) ) { + + $return["status"] = "error"; + $return["message"] = "Unable to create Absolute Path"; + } + } else { - die( formatJSEND( "error", "Unable to create Absolute Path" ) ); + if ( ! is_writable( $path ) || ! is_readable( $path ) ) { + + $return["status"] = "error"; + $return["message"] = "No Read/Write Permission"; + } } } else { - if ( ! is_writable( $this->path ) || ! is_readable( $this->path ) ) { - - die( formatJSEND( "error", "No Read/Write Permission" ) ); - } + $return["status"] = "error"; + $return["message"] = "Absolute Paths are only allowed for admins"; } } - $this->projects[] = array( "name" => $this->name, "path" => $this->path ); - $this->add_project( $this->name, $this->path ); - // Pull from Git Repo? - if ( $this->gitrepo && filter_var( $this->gitrepo, FILTER_VALIDATE_URL ) !== false ) { + if( $return["status"] == null ) { - $this->gitbranch = $this->SanitizeGitBranch(); - if ( ! $this->isAbsPath( $this->path ) ) { + $this->projects[] = array( "name" => $name, "path" => $path ); + $result = $this->add_project( $name, $path, $owner ); + + if( $result > 0 ) { - $this->command_exec = "cd " . escapeshellarg( WORKSPACE . '/' . $this->path ) . " && git init && git remote add origin " . escapeshellarg( $this->gitrepo ) . " && git pull origin " . escapeshellarg( $this->gitbranch ); + $return["status"] = "success"; + $return["message"] = "Created Project"; + $return["data"] = array( "name" => $name, "path" => $path ); } else { - $this->command_exec = "cd " . escapeshellarg( $this->path ) . " && git init && git remote add origin " . escapeshellarg( $this->gitrepo ) . " && git pull origin " . escapeshellarg( $this->gitbranch ); + $return["status"] = "error"; + $return["message"] = "A Project With the Same Name or Path Exists"; } - $this->ExecuteCMD(); } - - echo formatJSEND( "success", array( "name" => $this->name, "path" => $this->path ) ); } else { - echo formatJSEND( "error", "A Project With the Same Name or Path Exists" ); + $return["status"] = "error"; + $return["message"] = "A Project With the Same Name or Path Exists"; } } else { - echo formatJSEND( "error", "Project Name/Folder not allowed" ); + $return["status"] = "error"; + $return["message"] = "Project Name/Folder not allowed"; } } else { - echo formatJSEND( "error", "Project Name/Folder is empty" ); + $return["status"] = "error"; + $return["message"] = "Project Name/Folder is empty"; } + + return $return; } ////////////////////////////////////////////////////////////////// @@ -495,37 +589,26 @@ class Project extends Common { public function Delete() { - global $sql; - $query = "DELETE FROM projects WHERE path=? AND ( owner=? OR owner='nobody' );"; - $bind_variables = array( $this->path, $_SESSION["user"] ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); - - if( $return > 0 ) { + if( Permissions::has_owner( $this->path ) ) { - echo( formatJSEND( "success", "Successfully deleted $project_name" ) ); + global $sql; + $query = "DELETE FROM projects WHERE path=?"; + $bind_variables = array( $this->path ); + $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + + if( $return > 0 ) { + + exit( formatJSEND( "success", "Successfully deleted project." ) ); + } else { + + exit( formatJSEND( "error", "Error deleting project" ) ); + } } else { - echo formatJSEND( "error", "Error deleting project $project_name" ); + exit( formatJSEND( "error", "You do not have permission to delete this project" ) ); } } - ////////////////////////////////////////////////////////////////// - // Check Duplicate - ////////////////////////////////////////////////////////////////// - - public function CheckDuplicate() { - - $pass = true; - foreach ( $this->projects as $project => $data ) { - - if ( $data['name'] == $this->name || $data['path'] == $this->path ) { - - $pass = false; - } - } - return $pass; - } - ////////////////////////////////////////////////////////////////// // Sanitize Path ////////////////////////////////////////////////////////////////// @@ -533,7 +616,13 @@ class Project extends Common { public function SanitizePath() { $sanitized = str_replace( " ", "_", $this->path ); - return preg_replace( '/[^\w-]/', '', strtolower( $sanitized ) ); + return preg_replace( Filemanager::PATH_REGEX, '', strtolower( $sanitized ) ); + } + + public function sanitize_path( $path ) { + + $sanitized = str_replace( " ", "_", $path ); + return preg_replace( Filemanager::PATH_REGEX, '', strtolower( $sanitized ) ); } ////////////////////////////////////////////////////////////////// @@ -554,31 +643,16 @@ class Project extends Common { return $path; } - ////////////////////////////////////////////////////////////////// - // Execute Command - ////////////////////////////////////////////////////////////////// - - public function ExecuteCMD() { + public function clean_path( $path ) { - if ( function_exists( 'system' ) ) { + // prevent Poison Null Byte injections + $path = str_replace( chr( 0 ), '', $path ); + + // prevent go out of the workspace + while( strpos( $path, '../' ) !== false ) { - ob_start(); - system( $this->command_exec ); - ob_end_clean(); - } elseif( function_exists( 'passthru' ) ) { - - //passthru - ob_start(); - passthru($this->command_exec); - ob_end_clean(); - } elseif ( function_exists( 'exec' ) ) { - - //exec - exec( $this->command_exec, $this->output ); - } elseif ( function_exists( 'shell_exec' ) ) { - - //shell_exec - shell_exec( $this->command_exec ); + $path = str_replace( '../', '', $path ); } + return $path; } } \ No newline at end of file diff --git a/components/project/controller.php b/components/project/controller.php index 0af8186..4ccbf71 100755 --- a/components/project/controller.php +++ b/components/project/controller.php @@ -32,30 +32,36 @@ if( $_GET['action'] == 'add_user' ) { "undefined" ); - if( ! in_array( $_GET['username'], $invalid_users ) ) { + if( ! isset( $_GET['access'] ) || in_array( $_GET['access'], $invalid_users ) || ! in_array( $_GET['access'], array_keys( Permissions::LEVELS ) ) ) { - $Project->user = $_GET['username']; + exit( formatJSEND( "error", "No access set." ) ); } else { - echo formatJSEND( "error", "No username set." ); - return; + $access = Permissions::LEVELS[$_GET['access']]; } - if( $_GET['project_path'] != '' ) { + if( isset( $_GET['user_id'] ) && ! in_array( $_GET['user_id'], $invalid_users ) ) { - $Project->path = $_GET['project_path']; + $user = $_GET['user_id']; } else { - echo formatJSEND( "error", "No project path set." ); - return; + exit( formatJSEND( "error", "No user id set." ) ); } - if( $Project->check_owner( $_GET["project_path"], true ) ) { + if( isset( $_GET['project_path'] ) && $_GET['project_path'] != '' ) { - $Project->add_user(); + $project = $_GET['project_path']; } else { - echo formatJSEND( "error", "You can not manage this project." ); + exit( formatJSEND( "error", "No project path set." ) ); + } + + if( $Project->check_owner( $_GET['project_path'], true ) ) { + + exit( json_encode( $Project->add_user( $project, $user, $access ) ) ); + } else { + + exit( formatJSEND( "error", "You can not manage this project." ) ); } } @@ -65,28 +71,12 @@ if( $_GET['action'] == 'add_user' ) { ////////////////////////////////////////////////////////////////// if( $_GET['action'] == 'create' ) { - - $Project->name = $_GET['project_name']; - if( $_GET['public_project'] == 'true' ) { - - $Project->public_project = true; - } - - if( $_GET['project_path'] != '' ) { - - $Project->path = $_GET['project_path']; - } else { - - $Project->path = $_GET['project_name']; - } - // Git Clone? - if( ! empty( $_GET['git_repo'] ) ) { - - $Project->gitrepo = $_GET['git_repo']; - $Project->gitbranch = $_GET['git_branch']; - } - $Project->Create(); + $name = $_GET['project_name']; + $public = ( $_GET['public_project'] != 'true' ) ? false : true; + $path = ( $_GET['project_path'] != '' ) ? $_GET['project_path'] : $_GET['project_name']; + $return = $Project->Create( $path, $name, $public ); + exit( json_encode( $return ) ); } ////////////////////////////////////////////////////////////////// @@ -110,7 +100,7 @@ if( $_GET['action'] == 'current' ) { if( $_GET['action'] == 'delete' ) { - if( checkPath( $_GET['project_path'] ) ) { + if( isset( $_GET['project_path'] ) ) { $Project->path = $_GET['project_path']; $Project->Delete(); @@ -123,8 +113,7 @@ if( $_GET['action'] == 'delete' ) { if( $_GET['action'] == 'get_access' ) { - $Project->path = $_GET['project_path']; - $access = $Project->get_access( $_GET['project_path'] ); + $access = $Project->get_access( $_GET['project_id'] ); echo formatJSEND( "success", $access ); } @@ -147,7 +136,15 @@ if( $_GET['action'] == 'get_current' ) { $Project->no_return = true; } - $Project->GetFirst(); + $project = $Project->GetFirst(); + + if( $project == null ) { + + exit( formatJSEND( "error", "Error, Could not load a projet." ) ); + } else { + + exit( formatJSEND( "success", $project ) ); + } } else { // Load current @@ -155,7 +152,7 @@ if( $_GET['action'] == 'get_current' ) { $project_name = $Project->GetName(); if( ! $no_return ) { - echo formatJSEND( "success", array( "name" => $project_name, "path" => $_SESSION['project'] ) ); + exit( formatJSEND( "success", array( "name" => $project_name, "path" => $_SESSION['project'] ) ) ); } } } @@ -184,7 +181,7 @@ if( $_GET['action'] == 'get_owner' ) { if( $_GET['action'] == 'open' ) { - if( ! checkPath( $_GET['path'] ) ) { + if( ! isset( $_GET['path'] ) || ! Permissions::has_read( $_GET['path'] ) ) { die( formatJSEND( "error", "No Access to path " . $_GET['path'] ) ); } @@ -200,30 +197,36 @@ if( $_GET['action'] == 'remove_user' ) { "undefined" ); - if( ! in_array( $_GET['username'], $invalid ) ) { - - $Project->user = $_GET['username']; - } else { - - echo formatJSEND( "error", "No username set." ); - return; - } - - if( ! in_array( $_GET['project_path'], $invalid ) ) { + if( isset( $_GET["project_path"] ) && ! in_array( $_GET['project_path'], $invalid ) ) { $Project->path = $_GET['project_path']; } else { - echo formatJSEND( "error", "No project path set." ); - return; + exit( formatJSEND( "error", "No project path set." ) ); + } + + if( isset( $_GET["project_id"] ) && ! in_array( $_GET['project_id'], $invalid ) ) { + + $Project->project_id = $_GET['project_id']; + } else { + + exit( formatJSEND( "error", "No project id set." ) ); + } + + if( isset( $_GET["user_id"] ) && ! in_array( $_GET['user_id'], $invalid ) ) { + + $user_id = $_GET["user_id"]; + } else { + + exit( formatJSEND( "error", "No user id set." ) ); } if( $Project->check_owner( $_GET["project_path"], true ) ) { - $Project->remove_user(); + $Project->remove_user( $user_id ); } else { - echo formatJSEND( "error", "You can not manage this project." ); + exit( formatJSEND( "error", "You can not manage this project." ) ); } } @@ -233,7 +236,7 @@ if( $_GET['action'] == 'remove_user' ) { if( $_GET['action'] == 'rename' ) { - if( ! checkPath( $_GET['project_path'] ) ) { + if( ! isset( $_GET['project_path'] ) || ! Permissions::has_owner( $_GET['project_path'] ) ) { die( formatJSEND( "error", "No Access" ) ); } diff --git a/components/project/dialog.php b/components/project/dialog.php index c71e75f..cf952df 100755 --- a/components/project/dialog.php +++ b/components/project/dialog.php @@ -7,8 +7,9 @@ */ -require_once('../../common.php'); -require_once('./class.project.php'); +require_once( '../../common.php' ); +require_once( './class.project.php' ); +require_once( '../user/class.user.php' ); ////////////////////////////////////////////////////////////////// // Verify Session or Key @@ -56,7 +57,12 @@ switch( $_GET['action'] ) { case 'list': //Get projects data - $projects = $Project->get_projects(); + if( isset( $_GET["all"] ) ) { + + $projects = $Project->get_all_projects(); + } else { + $projects = $Project->get_projects(); + } ?>
    @@ -72,56 +78,63 @@ switch( $_GET['action'] ) {
    $data ) { + if( is_array( $projects ) ) { - $show = true; - if( $show ) { + foreach( $projects as $project => $data ) { - ?> - - - - - get_owner( $data['path'] ); - if( $owner == 'nobody' ) { - - ?> - - - - - - + + + + + get_owner( $data['path'] ); + if( $owner == -1 ) { + + ?> + + + + + + + + + + + + + + - - - - - - - -
    @@ -196,34 +209,55 @@ switch( $_GET['action'] ) { */ if( ! isset( $_GET["path"] ) || ! $Project->check_owner( $_GET["path"], true ) ) { ?> -
    Error, you either do not own this project or it is a public project.
    +

    Error, you either do not own this project or it is a public project.

    + get_project( $path ); - $access = json_decode( $project["access"], true ); + $access = $Project->get_access( $project["id"] ); $users = get_users( "return", true ); + $user = $User->get_user( $_SESSION["user"] ); + + if( isset( $users["status"] ) && $users["status"] == "error" ) { + + ?> +

    Error, could not fetch users information.

    + + +

    Error, You must have more than one user registered in your Codiad instance to manage permissions.

    + + -
    - + + + ">

    No users have been given access.

    @@ -233,15 +267,47 @@ switch( $_GET['action'] ) { ?> $user_permissions ) { + + $i = null; + + foreach( $users as $r => $current_user ) { + + if( $current_user["id"] == $user_permissions["user"] ) { + + $i = $current_user; + break; + } + } + + if( ! $i ) { + + continue; + } ?> + query( $query, $bind_variables, 0, "rowCount" ); } else { - $query = "DELETE FROM options WHERE name=? AND username=?"; + $query = array( + "*" => "DELETE FROM options WHERE name=? AND user=?", + "pgsql" => 'DELETE FROM options WHERE name=? AND "user"=?', + ); $bind_variables = array( $option, $this->username, @@ -138,17 +141,20 @@ class Settings { $query = "SELECT value FROM options WHERE name=?;"; $bind_variables = array( $option ); - $return = $sql->query( $query, $bind_variables, array() )[0]; + $return = $sql->query( $query, $bind_variables, array() ); } else { - $query = "SELECT value FROM user_options WHERE name=? AND username=?;"; - $bind_variables = array( $option, $this->username ); - $return = $sql->query( $query, $bind_variables, array() )[0]; + $query = array( + "*" => "SELECT value FROM user_options WHERE name=? AND user=?;", + "pgsql" => 'SELECT value FROM user_options WHERE name=? AND "user"=?;', + ); + $bind_variables = array( $option, $_SESSION["user_id"] ); + $return = $sql->query( $query, $bind_variables, array() ); } if( ! empty( $return ) ) { - $return = $return["value"]; + $return = $return[0]["value"]; } else { $return = null; @@ -172,8 +178,11 @@ class Settings { global $sql; - $query = "SELECT name, value FROM user_options WHERE username=?;"; - $bind_variables = array( $this->username ); + $query = array( + "*" => "SELECT name, value FROM user_options WHERE user=?;", + "pgsql" => 'SELECT name, value FROM user_options WHERE "user"=?;', + ); + $bind_variables = array( $_SESSION["user_id"] ); $return = $sql->query( $query, $bind_variables, array() ); $options = array(); @@ -220,7 +229,10 @@ class Settings { public function Load() { global $sql; - $query = "SELECT DISTINCT * FROM user_options WHERE username=?;"; + $query = array( + "*" => "SELECT DISTINCT * FROM user_options WHERE user=?;", + "pgsql" => 'SELECT DISTINCT * FROM user_options WHERE "user"=?;', + ); $bind_variables = array( $this->username ); @@ -241,7 +253,7 @@ class Settings { global $sql; if( $user_setting == null ) { - $query = "INSERT INTO options ( name, username, value ) VALUES ( ?, ? );"; + $query = "INSERT INTO options ( name, value ) VALUES ( ?, ? );"; $bind_variables = array( $option, $value, @@ -259,21 +271,27 @@ class Settings { } } else { - $query = "INSERT INTO user_options ( name, username, value ) VALUES ( ?, ?, ? );"; + $query = array( + "*" => "UPDATE user_options SET value=? WHERE name=? AND user=?;", + "pgsql" => 'UPDATE user_options SET value=? WHERE name=? AND "user"=?;', + ); $bind_variables = array( - $option, - $this->username, $value, + $option, + $_SESSION["user_id"], ); $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $result == 0 ) { - $query = "UPDATE user_options SET value=? WHERE name=? AND username=?;"; + $query = array( + "*" => "INSERT INTO user_options ( name, user, value ) VALUES ( ?, ?, ? );", + "pgsql" => 'INSERT INTO user_options ( name, "user", value ) VALUES ( ?, ?, ? );', + ); $bind_variables = array( - $value, $option, - $this->username, + $_SESSION["user_id"], + $value, ); $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); } diff --git a/components/settings/init.js b/components/settings/init.js index cd28450..3f4d0ee 100755 --- a/components/settings/init.js +++ b/components/settings/init.js @@ -166,7 +166,7 @@ let _self = codiad.settings; jQuery.ajax({ - + url: this.controller + '?action=update_option', type: "POST", dataType: 'html', diff --git a/components/settings/settings.editor.php b/components/settings/settings.editor.php index ecea254..e627910 100755 --- a/components/settings/settings.editor.php +++ b/components/settings/settings.editor.php @@ -44,8 +44,8 @@ require_once('../../common.php'); @@ -53,8 +53,8 @@ require_once('../../common.php'); diff --git a/components/sql/class.sql.conversions.php b/components/sql/class.sql.conversions.php deleted file mode 100644 index ccea77d..0000000 --- a/components/sql/class.sql.conversions.php +++ /dev/null @@ -1,458 +0,0 @@ - array( - - "mysql" => "CREATE TABLE IF NOT EXISTS", - "pgsql" => "CREATE TABLE IF NOT EXISTS", - "sqlite" => "CREATE TABLE IF NOT EXISTS", - ), - - "delete" => array( - - "mysql" => "DELETE", - "pgsql" => "DELETE", - "sqlite" => "DELETE", - ), - - "find" => array( - - "mysql" => "LOCATE( %substring%, %string% )", - "pgsql" => "POSITION( %substring% in %string% )", - "sqlite" => "INSTR( %string%, %substring% )", - ), - - "select" => array( - - "mysql" => "SELECT", - "pgsql" => "SELECT", - "sqlite" => "SELECT", - ), - - "update" => array( - - "mysql" => "UPDATE", - "pgsql" => "UPDATE", - "sqlite" => "UPDATE", - ), - ); - - public $comparisons = array( - - "equal" => array( - - "mysql" => "=", - "pgsql" => "=", - "sqlite" => "=", - ), - - "less than" => array( - - "mysql" => "<", - "pgsql" => "<", - "sqlite" => "<", - ), - - "more than" => array( - - "mysql" => ">", - "pgsql" => ">", - "sqlite" => ">", - ), - - "not" => array( - - "mysql" => "!", - "pgsql" => "!", - "sqlite" => "!", - ), - - "not equal" => array( - - "mysql" => "!=", - "pgsql" => "!=", - "sqlite" => "!=", - ), - - "where" => array( - - "mysql" => "WHERE", - "pgsql" => "WHERE", - "sqlite" => "WHERE", - ), - ); - - public $data_types = array( - - "bool" => array( - - "mysql" => "BOOL", - "pgsql" => "BOOL", - "sqlite" => "BOOL", - ), - - "int" => array( - - "mysql" => "INT", - "pgsql" => "INT", - "sqlite" => "INT", - ), - - "string" => array( - - "mysql" => "VARCHAR(255)", - "pgsql" => "VARCHAR", - "sqlite" => "VARCHAR", - ), - - "text" => array( - - "mysql" => "TEXT", - "pgsql" => "TEXT", - "sqlite" => "TEXT", - ), - ); - - public $general = array( - - "from" => array( - - "mysql" => "FROM", - "pgsql" => "FROM", - "sqlite" => "FROM", - ), - ); - - public $specials = array( - - "id" => array( - - "mysql" => "NOT NULL AUTO_INCREMENT PRIMARY KEY", - "pgsql" => "SERIAL PRIMARY KEY", - "sqlite" => "SERIAL PRIMARY KEY", - ), - - "key" => array( - - "mysql" => "KEY", - "pgsql" => "KEY", - "sqlite" => "KEY", - ), - - "auto increment" => array( - - "mysql" => "AUTO_INCREMENT", - "pgsql" => "AUTO_INCREMENT", - "sqlite" => "AUTO_INCREMENT", - ), - - "not null" => array( - - "mysql" => "NOT NULL", - "pgsql" => "NOT NULL", - "sqlite" => "NOT NULL", - ), - - "null" => array( - - "mysql" => "NULL", - "pgsql" => "NULL", - "sqlite" => "NULL", - ), - - "unique" => array( - - "mysql" => "CONSTRAINT %constraint_name% UNIQUE ( %field_names% )", - "pgsql" => "CONSTRAINT %constraint_name% UNIQUE ( %field_names% )", - "sqlite" => "CONSTRAINT %constraint_name% UNIQUE ( %field_names% )", - ), - ); - - public $wraps = array( - - "close" => array( - - "mysql" => "`", - "mssql" => "]", - "pgsql" => "\"", - "sqlite" => "\"", - ), - - "open" => array( - - "mysql" => "`", - "mssql" => "[", - "pgsql" => "\"", - "sqlite" => "\"", - ), - ); - - public function check_field( $needle, $haystack ) { - - $field = preg_replace_callback( - // Matches parts to be replaced: '[field]' - '/(\[.*?\])/', - // Callback function. Use 'use()' or define arrays as 'global' - function( $matches ) use ( $haystack ) { - - // Remove square brackets from the match - // then use it as variable name - $match = trim( $matches[1], "[]" ); - return $match; - }, - // Input string to search in. - $needle - ); - - if( $field === $needle ) { - - $field = false; - } - return $field; - } - - public function find( $substring, $string ) { - - $dbtype = DBTYPE; - $find_string = $this->actions["find"][$dbtype]; - $find_string = str_replace( "%string%", $string, $find_string ); - $find_string = str_replace( "%substring%", $substring, $find_string ); - - return $find_string; - } - - public function select( $table, $fields, $where ) { - - $dbtype = DBTYPE; - $id_close = $this->wraps["close"][$dbtype]; - $id_open = $this->wraps["open"][$dbtype]; - $query = $this->actions["select"][$dbtype] . " "; - $bind_vars = array(); - - if( empty( $fields ) ) { - - $query .= " * "; - } - - foreach( $fields as $field ) { - - $query .= $field . ","; - } - - $query = substr( $query, 0, -1 ); - $query .= " {$this->general["from"][$dbtype]} {$table} "; - - if( ! empty( $where ) ) { - - $query .= " {$this->comparisons["where"][$dbtype]} "; - } - - foreach( $where as $comparison ) { - - $comparison_string = ""; - - //Put a replace of %% symbols with fields and open / close - if( $comparison[0] == "find" ) { - - $c1 = $this->check_field( $comparison[1], $fields ); - $c2 = $this->check_field( $comparison[2], $fields ); - $c3 = $this->check_field( $comparison[3][1], $fields ); - - if( ! $c1 === FALSE ) { - - $c1 = $id_open . $c1 . $id_close; - } else { - - $c1 = "?"; - array_push( $bind_vars, $comparison[1] ); - } - - if( ! $c2 === FALSE ) { - - $c2 = $id_open . $c2 . $id_close; - } else { - - $c2 = "?"; - array_push( $bind_vars, $comparison[2] ); - } - - if( ! $c3 === FALSE ) { - - $c3 = $id_open . $c3 . $id_close; - } else { - - $c3 = "?"; - array_push( $bind_vars, $comparison[3][1] ); - } - - $c0 = $this->find( $c1, $c2 ); - $comparison_string .= "{$c0} {$this->comparisons[$comparison[3][0]][$dbtype]} {$c3}"; - } elseif( $comparison[0] == "in" ) { - - - } elseif( $comparison[0] == "limit" ) { - - - } else { - - if( in_array( $fields, $comparison[1] ) ) { - - $comparison[1] = $id_open . $comparison[1] . $id_close; - } - - if( in_array( $fields, $comparison[3] ) ) { - - $comparison[3] = $id_open . $comparison[3] . $id_close; - } - - $comparison_string .= "{$comparison[1]} {$this->$comparisons[$comparison[0]][$dbtype]} {$comparison[2]}"; - } - - $index = array_search( $comparison, $where ); - - if( $index ) { - - } else { - - $query .= "{$comparison_string} "; - } - } - - //$query = substr( $query, 0, -1 ); - $query .= ";"; - return array( $query, $bind_vars ); - } - - public function table( $table_name, $fields, $attributes ) { - - $dbtype = DBTYPE; - $id_close = $this->wraps["close"][$dbtype]; - $id_open = $this->wraps["open"][$dbtype]; - - $query = "{$this->actions["create"][$dbtype]} {$table_name} ("; - - foreach( $fields as $id => $type ) { - - $query .= "{$id} {$this->data_types[$type][$dbtype]}"; - - if( isset( $attributes[$id] ) ) { - - foreach( $attributes[$id] as $attribute ) { - - $attribute_string = $this->specials["$attribute"][$dbtype]; - - if( $attribute == "unique" ) { - - continue; - } - - if( $dbtype == "pgsql" ) { - - if( $id == "id" ) { - - $query = substr( $query, 0, -( strlen( " {$this->data_types[$type][$dbtype]}" ) ) ); - } - } - - if( ! strpos( $attribute_string, "%table_name%" ) === FALSE ) { - - $attribute_string = str_replace( "%table_name%", $table_name, $attribute_string ); - } - - if( ! strpos( $attribute_string, "%fields%" ) === FALSE ) { - - $fields_string = ""; - - foreach( $fields as $field ) { - - $fields_string .= "{$id_open}field{$id_close},"; - } - - $fields_string = substr( $fields_string, 0, -1 ); - $attribute_string = str_replace( "%fields%", $fields_string, $attribute_string ); - } - $query .= " {$attribute_string}"; - } - } - $query .= ","; - } - - $id_close = $this->wraps["close"][$dbtype]; - $id_open = $this->wraps["open"][$dbtype]; - $fields_string = ""; - $unique_string = ""; - $unique_length = 0; - - foreach( $attributes as $id => $attribute ) { - - if( in_array( "unique", $attribute ) ) { - - $unique_length++; - } - } - - foreach( $attributes as $id => $attribute ) { - - if( is_array( $attribute ) && in_array( "unique", $attribute ) ) { - - if( $unique_string == "" ) { - - $unique_string = $this->specials["unique"][$dbtype] . ","; - } - if( $dbtype == "mysql" && $fields ) { - - if( $fields[$id] == "text" ) { - - $field_length = ( 3000 / $unique_length ); - $fields_string .= "{$id_open}{$id}{$id_close}($field_length),"; - } elseif( $fields[$id] == "string" ) { - - $field_length = ( 3000 / $unique_length ); - $fields_string .= "{$id_open}{$id}{$id_close}(255),"; - } - } else { - - $fields_string .= "{$id_open}{$id}{$id_close},"; - } - } - } - - $unique_string = str_replace( "%constraint_name%", strtolower( preg_replace( '#[^A-Za-z0-9' . preg_quote( '-_@. ').']#', '', $fields_string ) ), $unique_string ); - $unique_string = str_replace( "%field_names%", substr( $fields_string, 0, -1 ), $unique_string ); - $query .= $unique_string; - - $query = substr( $query, 0, -1 ); - $query .= ")"; - - if( $dbtype == "mysql" ) { - - $query .= " ENGINE=InnoDB;"; - } else { - - $query .= ";"; - } - - return( $query ); - } - - public function tables( $tables ) { - - $query = ""; - - foreach( $tables as $table_name => $table_data ) { - - $query .= $this->table( $table_name, $table_data["fields"], $table_data["attributes"] ) . PHP_EOL; - } - return( $query ); - } - - public function update( $table, $fields, $where ) { - - - } -} - -?> diff --git a/components/sql/class.sql.php b/components/sql/class.sql.php index f2b6579..4ff9c1d 100755 --- a/components/sql/class.sql.php +++ b/components/sql/class.sql.php @@ -1,6 +1,6 @@ conversions = new sql_conversions(); } public function close() { @@ -35,152 +32,45 @@ class sql { $dbtype = DBTYPE; $username = DBUSER; $password = DBPASS; + $options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); - $this->connection = new PDO( "{$dbtype}:host={$host};dbname={$dbname}", $username, $password ); + $this->connection = new PDO( "{$dbtype}:host={$host};dbname={$dbname}", $username, $password, $options ); } return( $this->connection ); } - public function create_table( $table_name, $fields=array(), $attributes=array() ) { - - $query = $this->conversions->table( $table_name, $fields, $attributes ); - //$this->query( $query, array(), array(), null, "rowCount" ); - } - public function create_default_tables() { - $result = $this->create_tables( - array( - "active" => array( - "fields" => array( - "username" => "string", - "path" => "text", - "position" => "string", - "focused" => "string" - ), - "attributes" => array( - "username" => array( "not null", "unique" ), - "path" => array( "not null", "unique" ), - "focused" => array( "not null" ), - ) - ), - "options" => array( - "fields" => array( - "id" => "int", - "name" => "string", - "value" => "text", - ), - "attributes" => array( - "id" => array( "id" ), - "name" => array( "not null", "unique" ), - "value" => array( "not null" ), - ) - ), - "projects" => array( - "fields" => array( - "id" => "int", - "name" => "string", - "path" => "text", - "owner" => "string", - "access" => "string", - ), - "attributes" => array( - - "id" => array( "id" ), - "name" => array( "not null" ), - "path" => array( "not null", "unique" ), - "owner" => array( "not null", "unique" ), - "access" => array(), - ) - ), - "users" => array( - "fields" => array( - "id" => "int", - "first_name" => "string", - "last_name" => "string", - "username" => "string", - "password" => "text", - "email" => "string", - "project" => "string", - "access" => "string", - "groups" => "string", - "token" => "string", - ), - "attributes" => array( - "id" => array( "id" ), - "username" => array( "not null", "unique" ), - "password" => array( "not null" ), - "access" => array( "not null" ), - ) - ), - "user_options" => array( - "fields" => array( - "id" => "int", - "name" => "string", - "username" => "string", - "value" => "text", - ), - "attributes" => array( - "id" => array( "id" ), - "name" => array( "not null", "unique" ), - "username" => array( "not null", "unique" ), - "value" => array( "not null" ), - ) - ), - ) + $create_tables = $this->create_tables(); + $structure_updates = $this->update_table_structure(); + $result = array( + "create_tables" => $create_tables, + "structure_updates" => $structure_updates ); - return $result; } - public function create_tables( $table ) { + public function create_tables() { - /** - Tables layout - array( - - "table_name" => array( - - "fields" => array( - - "id" => "int", - "test_field" => "string" - ), - "attributes" => array( - - "id" => array( "id" ), - "test_field" => array( "not null" ), - ) - ), - "table2_name" => array( - - "fields" => array( - - "id" => "int", - "test_field" => "string" - ), - "attributes" => array( - - "id" => array( "id" ), - "test_field" => array( "not null" ), - ) - ) - ); - */ + $script = __DIR__ . "/scripts/" . DBTYPE . ".sql"; - $query = $this->conversions->tables( $table ); - $connection = $this->connect(); - $result = $connection->exec( $query ); - $error = $connection->errorInfo(); - //echo var_dump( $query, $result, $connection->errorInfo() ) . "
    "; - - if ( $result === false || ! $error[0] == "00000" ) { + if( ! is_file( $script ) ) { - return $error; - } else { + return "Error, no database scripts specified for currently selected dbtype."; + } + + try { + $query = file_get_contents( $script ); + $connection = $this->connect(); + $result = $connection->exec( $query ); return true; + } catch( exception $error ) { + + return $error->getMessage(); } } @@ -213,67 +103,441 @@ class sql { return self::$instance; } - public function select( $table, $fields=array(), $where=array() ) { - - $array = $this->conversions->select( $table, $fields, $where ); - $query = $array[0]; - $bind_vars = $array[1]; - $result = $this->query( $query, $bind_vars, array() ); - //echo var_dump( $query, $bind_vars ) . "
    "; - return $result; - } - - public function update( $table, $fields=array(), $where=array() ) { - - $query = $this->conversions->update( $table, $fields, $where ); - //echo var_dump( $query ) . "
    "; - } - - public function query( $query, $bind_variables, $default, $action='fetchAll', $show_errors=false ) { + public function update_table_structure() { + $status_updates = array(); $connection = $this->connect(); - $statement = $connection->prepare( $query ); - $statement->execute( $bind_variables ); - - switch( $action ) { + + if( DBTYPE === "mysql" || DBTYPE === "pgsql" ) { - case( 'rowCount' ): + try { - $return = $statement->rowCount(); - break; + $access_query = array( + "mysql" => "INSERT INTO access( project, user, level ) VALUES ", + "pgsql" => 'INSERT INTO access( project, "user", level ) VALUES ', + ); + $projects = $this->query( "SELECT id, access FROM projects", array(), array(), "fetchAll", "exception" ); + $users = $this->query( "SELECT id, username FROM users", array(), array(), "fetchAll", "exception" ); + $delete = Permissions::LEVELS["delete"]; + + foreach( $users as $row => $user ) { + + foreach( $projects as $row => $project ) { + + $access = json_decode( $project["access"], true ); + if( ! is_array( $access ) || empty( $access ) ) { + + continue; + } + + foreach( $access as $granted_user ) { + + if( $granted_user == $user["username"] ) { + + $access_query[DBTYPE] .= "( {$project["id"]}, {$user["id"]}, $delete ),"; + } + } + } + } + + if( $access_query !== "INSERT INTO access( project, user, level ) VALUES " ) { + + $result = $this->query( substr( $access_query[DBTYPE], 0, -1 ), array(), 0, "rowCount", "exception" ); + } + $result = $this->query( "ALTER TABLE projects DROP COLUMN access", array(), 0, "rowCount" ); + $status_updates["access_column"] = "Cached data and removed access column."; + } catch( Exception $error ) { + + //The access field is not there. + //echo var_export( $error->getMessage(), $access_query ); + $status_updates["access_column"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "No access column to convert." + ); + } - case( 'fetchAll' ): + try { - $return = $statement->fetchAll( \PDO::FETCH_ASSOC ); - break; + $update_query = ""; + $projects = $this->query( "SELECT id, path FROM projects", array(), array(), "fetchAll", "exception" ); + $users = $this->query( "SELECT username,project FROM users", array(), array(), "fetchAll", "exception" ); + $convert = false; + $delete = Permissions::LEVELS["delete"]; + + foreach( $users as $row => $user ) { + + if( ! is_numeric( $user["project"] ) ) { + + $convert = true; + } + } + + if( $convert ) { + + //change project to users table + $result = $this->query( "ALTER TABLE users DROP COLUMN project", array(), array(), "rowCount", "exception" ); + $result = $this->query( "ALTER TABLE users ADD COLUMN project INT", array(), array(), "rowCount", "exception" ); + + foreach( $users as $row => $user ) { + + foreach( $projects as $row => $project ) { + + if( $project["path"] == $user["project"] ) { + + $result = $this->query( "UPDATE users SET project=? WHERE username = ?;", array( $project["id"], $user["username"] ), array(), "rowCount", "exception" ); + break; + } + } + } + } else { + + $status_updates["users_current_project"] = array( "dev_message" => "Users current project column to project_id conversion not needed." ); + } + } catch( Exception $error ) { + + //echo var_dump( $error->getMessage() ); + $status_updates["users_current_project"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Users current project column to project_id conversion failed." + ); + } - case( 'fetchColumn' ): + try { - $return = $statement->fetchColumn(); - break; + $this->query( array( + "mysql" => "ALTER TABLE user_options DROP INDEX name255username255;", + "pgsql" => "ALTER TABLE user_options DROP CONSTRAINT name255username255;", + ), array(), 0, "rowCount", "exception" ); + } catch( Exception $error ) { + + //The access field is not there. + //echo var_export( $error->getMessage(), $access_query ); + $status_updates["nameusername_user_option_constraint"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "No constriant to remove." + ); + } - default: + try { - $return = $statement->fetchAll( \PDO::FETCH_ASSOC ); - break; + $update_query = array( + "mysql" => "", + "pgsql" => "", + ); + $options = $this->query( "SELECT id, name, username, value FROM user_options", array(), array(), "fetchAll", "exception" ); + $users = $this->query( "SELECT id, username FROM users", array(), array(), "fetchAll", "exception" ); + $delete = Permissions::LEVELS["delete"]; + $convert = false; + + foreach( $users as $row => $user ) { + + foreach( $options as $row => $option ) { + + if( isset( $option["username"] ) ) { + + $convert = true; + break; + } + } + } + + if( $convert ) { + + //change project to users table + $result = $this->query( "ALTER TABLE user_options DROP COLUMN username", array(), array(), "rowCount", "exception" ); + $result = $this->query( array( + "mysql" => "ALTER TABLE user_options ADD COLUMN user INT", + "pgsql" => 'ALTER TABLE user_options ADD COLUMN "user" INT', + ), array(), array(), "rowCount", "exception" ); + + + foreach( $users as $row => $user ) { + + foreach( $options as $row => $option ) { + + if( $option["username"] == $user["username"] ) { + + $update_query = ""; + if( DBTYPE == "mysql" ) { + + $update_query = "UPDATE user_options SET user=? WHERE id=?;"; + } else { + + $update_query = "UPDATE user_options SET \"user\"=? WHERE id=?;"; + } + $result = $this->query( $update_query, array( $user["id"], $option["id"] ), array(), "rowCount", "exception" ); + } + } + } + } else { + + $status_updates["username_user_option_column"] = array( "dev_message" => "User options username column needed no conversion." ); + } + } catch( Exception $error ) { + + //The access field is not there. + //echo var_export( $error->getMessage(), $access_query ); + $status_updates["username_user_option_column"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "No username column to convert." + ); + } + + try { + + $result = $this->query( "ALTER TABLE users DROP COLUMN groups", array(), array(), "rowCount", "exception" ); + $status_updates["users_groups_column"] = array( "dev_message" => "Removal of the groups column from the users table succeeded." ); + } catch( Exception $error ) { + + //echo var_dump( $error->getMessage() ); + $status_updates["users_groups_column"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Removal of the groups column from the users table failed. This usually means there was never one to begin with" + ); + } + + try { + + $update_query = ""; + $users = $this->query( "SELECT id, access FROM users", array(), array(), "fetchAll", "exception" ); + $convert = false; + + foreach( $users as $row => $user ) { + + if( ! is_numeric( $user["access"] ) ) { + + $convert = true; + break; + } + } + + if( $convert ) { + + //change project to users table + $result = $this->query( "ALTER TABLE users DROP COLUMN access", array(), array(), "rowCount", "exception" ); + $result = $this->query( "ALTER TABLE users ADD COLUMN access INT", array(), array(), "rowCount", "exception" ); + + foreach( $users as $row => $user ) { + + if( in_array( $user["access"], array_keys( Permissions::SYSTEM_LEVELS ) ) ) { + + $access = Permissions::SYSTEM_LEVELS[$user["access"]]; + } elseif( is_numeric( $user["access"] ) ) { + + $access = $user["access"]; + } else { + + $access = Permissions::SYSTEM_LEVELS["user"]; + } + $update_query = "UPDATE users SET access=? WHERE id=?;"; + $this->query( $update_query, array( $access, $user["id"] ), array(), "rowCount", "exception" ); + } + } else { + + $status_updates["users_access_column"] = array( "dev_message" => "No update needed." ); + } + } catch( Exception $error ) { + + $status_updates["path_owner_constraint"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Error changing access column from varchar to int" + ); + } + + try { + + $projects = $this->query( array( + "mysql" => "ALTER TABLE projects DROP INDEX path1500owner255;", + "pgsql" => "ALTER TABLE projects DROP CONSTRAINT path1500owner255;", + ), array(), 0, "rowCount", "exception" ); + } catch( Exception $error ) { + + //echo var_dump( $error->getMessage() ); + $status_updates["path_owner_constraint"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Removal of path1500owner255 constraint in the projects table failed. This usually means there was never one to begin with" + ); + } + + try { + + $convert = false; + $update_query = ""; + $projects = $this->query( "SELECT id, name, path, owner FROM projects", array(), array(), "fetchAll", "exception" ); + $users = $this->query( "SELECT id, username FROM users", array(), array(), "fetchAll", "exception" ); + $delete = Permissions::LEVELS["delete"]; + + foreach( $projects as $row => $project ) { + + if( ! is_numeric( $project["owner"] ) ) { + + $convert = true; + } + } + + if( $convert ) { + + //change project to users table + $result = $this->query( "ALTER TABLE projects DROP COLUMN owner", array(), array(), "rowCount", "exception" ); + $result = $this->query( "ALTER TABLE projects ADD COLUMN owner INT", array(), array(), "rowCount", "exception" ); + + foreach( $projects as $row => $project ) { + + $current_user = null; + $bind_variables = array(); + + foreach( $users as $row => $user ) { + + if( $project["owner"] == $user["username"] ) { + + $current_user = $user; + break; + } + } + + if( $current_user == null || $project["owner"] != $current_user["username"] ) { + + $update_query = "UPDATE projects SET owner=? WHERE id=?;"; + $bind_variables[] = -1; + $bind_variables[] = $project["id"]; + } else { + + $update_query = "UPDATE projects SET owner=? WHERE id=?;"; + $bind_variables[] = $user["id"]; + $bind_variables[] = $project["id"]; + } + + $result = $this->query( $update_query, $bind_variables, array(), "rowCount", "exception" ); + } + } else { + + $status_updates["owner_projects_column"] = array( "dev_message" => "User projects owner column needed no conversion." ); + } + } catch( Exception $error ) { + + //The access field is not there. + //echo var_export( $error->getMessage(), $access_query ); + $status_updates["username_user_option_column"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "No username column to convert." + ); + } + + try { + + $projects = $this->query( array( + "mysql" => "ALTER TABLE active DROP INDEX username255path1500;", + "pgsql" => "ALTER TABLE active DROP CONSTRAINT username255path1500;", + ), array(), 0, "rowCount", "exception" ); + } catch( Exception $error ) { + + //echo var_dump( $error->getMessage() ); + $status_updates["username_path_constraint"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Removal of username255path1500 constraint in the active table failed. This usually means there was never one to begin with" + ); + } + + try { + + $result = $this->query( "DELETE FROM active;", array(), 0, "rowCount", "exception" ); + $result = $this->query( "ALTER TABLE active DROP COLUMN username;", array(), 0, "rowCount", "exception" ); + $result = $this->query( array( + "mysql" => "ALTER TABLE active ADD COLUMN user INT", + "pgsql" => 'ALTER TABLE active ADD COLUMN "user" INT', + ), array(), array(), "rowCount", "exception" ); + } catch( Exception $error ) { + + //echo var_dump( $error->getMessage() ); + $status_updates["username_active_coluin"] = array( + "error_message" => $error->getMessage(), + "dev_message" => "Removal of username255path1500 constraint in the active table failed. This usually means there was never one to begin with" + ); + } + } + return $status_updates; + } + + public function query( $query, $bind_variables, $default, $action='fetchAll', $errors="default" ) { + + /** + * Errors: + * default - this value could be anything such as true or foobar + * message + * exception + */ + + if( is_array( $query ) ) { + + if( in_array( DBTYPE, array_keys( $query ) ) ) { + + $query = $query[DBTYPE]; + } else { + + if( isset( $query["*"] ) ) { + + $query = $query["*"]; + } else { + + $return = $default; + + if( $errors == "message" ) { + + $return = json_encode( array( "error" => "No query specified for database type." ) ); + } elseif( $errors == "exception" ) { + + throw new Error( "No query specified for database type." ); + } + return $return; + } + } } - $error = $statement->errorInfo(); - - if( ! $error[0] == "00000" ) { + try { + + $connection = $this->connect(); + $statement = $connection->prepare( $query ); + $statement->execute( $bind_variables ); + + switch( $action ) { + + case( 'rowCount' ): + + $return = $statement->rowCount(); + break; + + case( 'fetch' ): + + $return = $statement->fetch( \PDO::FETCH_ASSOC ); + break; + + case( 'fetchAll' ): + + $return = $statement->fetchAll( \PDO::FETCH_ASSOC ); + break; + + case( 'fetchColumn' ): + + $return = $statement->fetchColumn(); + break; + + default: + + $return = $statement->fetchAll( \PDO::FETCH_ASSOC ); + break; + } + } catch( exception $error ) { - echo var_export( $error ); - echo var_export( $return ); $return = $default; - } - - if( $show_errors ) { - $return = json_encode( $error ); + if( $errors == "message" ) { + + $return = json_encode( array( $error->getMessage() ) ); + } elseif( $errors == "exception" ) { + + throw $error; + } } - - //echo var_dump( $error, $return ); - $this->close(); return( $return ); } diff --git a/components/sql/scripts/mysql.sql b/components/sql/scripts/mysql.sql new file mode 100644 index 0000000..5582831 --- /dev/null +++ b/components/sql/scripts/mysql.sql @@ -0,0 +1,79 @@ +-- +-- Table structure for table `access` +-- + +CREATE TABLE IF NOT EXISTS `access` ( + `user` int NOT NULL, + `project` int NOT NULL, + `level` int NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Table structure for table `active` +-- + +CREATE TABLE IF NOT EXISTS `active` ( + `user` int NOT NULL, + `path` text NOT NULL, + `position` varchar(255) DEFAULT NULL, + `focused` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `options` +-- + +CREATE TABLE IF NOT EXISTS `options` ( + `id` int PRIMARY KEY AUTO_INCREMENT NOT NULL, + `name` varchar(255) UNIQUE NOT NULL, + `value` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `projects` +-- + +CREATE TABLE IF NOT EXISTS `projects` ( + `id` int PRIMARY KEY AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `path` text NOT NULL, + `owner` int NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +CREATE TABLE IF NOT EXISTS `users` ( + `id` int PRIMARY KEY AUTO_INCREMENT NOT NULL, + `first_name` varchar(255) DEFAULT NULL, + `last_name` varchar(255) DEFAULT NULL, + `username` varchar(255) NOT NULL, + `password` text NOT NULL, + `email` varchar(255) DEFAULT NULL, + `project` int DEFAULT NULL, + `access` int NOT NULL, + `token` varchar(255) DEFAULT NULL, + UNIQUE KEY `username` ( `username` ) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_options` +-- + +CREATE TABLE IF NOT EXISTS `user_options` ( + `id` int PRIMARY KEY AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `user` int NOT NULL, + `value` text NOT NULL, + UNIQUE KEY `name_user` (`name`,`user`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/components/sql/scripts/pgsql.sql b/components/sql/scripts/pgsql.sql new file mode 100644 index 0000000..1a3aea8 --- /dev/null +++ b/components/sql/scripts/pgsql.sql @@ -0,0 +1,78 @@ +-- +-- Table structure for table active +-- + +CREATE TABLE IF NOT EXISTS active ( + "user" integer NOT NULL, + path text NOT NULL, + position varchar(255) DEFAULT NULL, + focused varchar(255) NOT NULL +); + +-- +-- Table structure for table access +-- + +CREATE TABLE IF NOT EXISTS access ( + "user" integer NOT NULL, + project integer NOT NULL, + level integer NOT NULL +); + +-- -------------------------------------------------------- + +-- +-- Table structure for table options +-- + +CREATE TABLE IF NOT EXISTS options ( + id SERIAL PRIMARY KEY, + name varchar(255) UNIQUE NOT NULL, + value text NOT NULL +); + +-- -------------------------------------------------------- + +-- +-- Table structure for table projects +-- + +CREATE TABLE IF NOT EXISTS projects ( + id SERIAL PRIMARY KEY, + name varchar(255) NOT NULL, + path text NOT NULL, + owner integer NOT NULL +); + + +-- -------------------------------------------------------- + +-- +-- Table structure for table users +-- + +CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + first_name varchar(255) DEFAULT NULL, + last_name varchar(255) DEFAULT NULL, + username varchar(255) UNIQUE NOT NULL, + password text NOT NULL, + email varchar(255) DEFAULT NULL, + project integer DEFAULT NULL, + access integer NOT NULL, + token varchar(255) DEFAULT NULL +); + +-- -------------------------------------------------------- + +-- +-- Table structure for table user_options +-- + +CREATE TABLE IF NOT EXISTS user_options ( + id SERIAL PRIMARY KEY, + name varchar(255) NOT NULL, + "user" integer NOT NULL, + value text NOT NULL, + UNIQUE (name,"user") +); diff --git a/components/system/controller.php b/components/system/controller.php index c1ffdac..fd8bdc3 100644 --- a/components/system/controller.php +++ b/components/system/controller.php @@ -1,9 +1,18 @@ create_default_tables(); - - if( $result === true ) { + if( is_admin() ) { - exit( formatJSEND( "success", "Created tables." ) ); + global $sql; + $result = $sql->create_default_tables(); + + //echo var_dump( $result ); + + if( $result === true ) { + + exit( formatJSEND( "success", "Created tables." ) ); + } else { + + exit( formatJSEND( "error", array( "message" => "Could not create tables.", "result" => $result ) ) ); + } } else { - exit( formatJSEND( "error", "Could not create tables." ) ); + exit( formatJSEND( "error", "Only admins can use this method." ) ); } - } else { + break; + + case( "get_ini_setting" ): - exit( formatJSEND( "error", "Only admins can use this method." ) ); - } + if( isset( $_POST["option"] ) ) { + + $option = $_POST["option"]; + } else { + + $option = $_GET["option"]; + } + + exit( json_encode( array( + "option" => $option, + "value" => ini_get( $option ), + ))); + break; } \ No newline at end of file diff --git a/components/update/class.update.php b/components/update/class.update.php index 8b2fdb4..1bf6df1 100755 --- a/components/update/class.update.php +++ b/components/update/class.update.php @@ -12,7 +12,7 @@ class Update { // CONSTANTS ////////////////////////////////////////////////////////////////// - CONST VERSION = "v.2.9.5"; + CONST VERSION = "v.3.0.0"; ////////////////////////////////////////////////////////////////// // PROPERTIES diff --git a/components/update/dialog.php b/components/update/dialog.php index f78237b..21c8731 100755 --- a/components/update/dialog.php +++ b/components/update/dialog.php @@ -66,11 +66,11 @@ switch($_GET['action']){ ?>

    .
    ' + i18n("Contacting GitHub...") + '

    '); + $('#modal-content').html('
    ' + i18n("Contacting Git Server...") + '

    '); }, check_for_update: function () { diff --git a/components/update/update.php b/components/update/update.php index dc2d681..c75ec30 100755 --- a/components/update/update.php +++ b/components/update/update.php @@ -8,13 +8,28 @@ require_once('../../common.php'); require_once('../settings/class.settings.php'); require_once('./class.update.php'); - +function check_access_legacy() { + + $pass = false; + + if( isset( $_SESSION["token"] ) && isset( $_SESSION["user"] ) ) { + + global $sql; + $query = "SELECT COUNT( * ) FROM users WHERE username=? AND access=?;"; + $bind_variables = array( $_SESSION["user"], "admin" ); + $return = $sql->query( $query, $bind_variables, -1, 'fetchColumn' ); + $admin = ( $return > 0 ); + return $admin; + } + return $pass; +} $user_settings_file = BASE_PATH . "/data/settings.php"; $projects_file = BASE_PATH . "/data/projects.php"; $users_file = BASE_PATH . "/data/users.php"; //checkSession(); -if ( ! checkAccess() ) { +if ( ! checkAccess() && ! check_access_legacy() ) { + echo "Error, you do not have access to update Codiad."; exit(); } @@ -177,6 +192,12 @@ class updater { $sql = new sql(); $connection = $sql->connect(); $result = $sql->create_default_tables(); + $upgrade_function = str_replace( ".", "_", $this->update::VERSION ); + + if( is_callable( array( $this, $upgrade_function ) ) ) { + + $this->$upgrade_function(); + } } function check_update() { @@ -453,7 +474,6 @@ class updater { $this->backup(); - try { $sessions = "../../data/sessions"; @@ -476,10 +496,14 @@ class updater { $this->path . "/components/autocomplete", $this->path . "/plugins/auto_save", + $this->path . "/plugins/Codiad-Archives", + $this->path . "/plugins/Codiad-Archives-master", $this->path . "/plugins/Codiad-Auto-Save", $this->path . "/plugins/Codiad-Auto-Save-master", $this->path . "/plugins/Codiad-CodeSettings", $this->path . "/plugins/Codiad-CodeSettings-master", + $this->path . "/plugins/Codiad-DragDrop", + $this->path . "/plugins/Codiad-DragDrop-master", ); foreach( $folder_conflictions as $dir ) { @@ -513,7 +537,9 @@ class updater { $this->path . "/{$update_folder}/.gitignore", $this->path . "/.gitlab-ci.yml", - $this->path . "/{$update_folder}/.gitlab-ci.yml" + $this->path . "/{$update_folder}/.gitlab-ci.yml", + + $this->path . "/components/sql/class.sql.conversions.php", ); foreach( $file_conflictions as $file ) { @@ -923,7 +949,7 @@ if( isset( $_GET["action"] ) && $_GET["action"] !== '' ) { return; } - progress.innerText = "Filesystem update finished. Please wait, your browser will now reload and start the datbase update."; + progress.innerText = "Filesystem update finished. Please wait, your browser will now reload and start the database update."; setTimeout( function() { @@ -992,7 +1018,7 @@ if( isset( $_GET["action"] ) && $_GET["action"] !== '' ) { return; } - progress.innerText = "Filesystem update finished. Please wait, your browser will now reload and start the datbase update."; + progress.innerText = "Filesystem update finished. Please wait, your browser will now reload and start the database update."; setTimeout( function() { diff --git a/components/user/class.user.php b/components/user/class.user.php index 78d2586..77a7932 100755 --- a/components/user/class.user.php +++ b/components/user/class.user.php @@ -10,25 +10,10 @@ require_once( "../settings/class.settings.php" ); class User { - const ACCESS = array( - "admin", - "user" - ); - ////////////////////////////////////////////////////////////////// // PROPERTIES ////////////////////////////////////////////////////////////////// - public $access = 'user'; - public $username = ''; - public $password = ''; - public $project = ''; - public $projects = ''; - public $users = ''; - public $actives = ''; - public $lang = ''; - public $theme = ''; - ////////////////////////////////////////////////////////////////// // METHODS ////////////////////////////////////////////////////////////////// @@ -43,62 +28,65 @@ class User { } - public function add_user() { + public function add_user( $username, $password, $access ) { global $sql; $query = "INSERT INTO users( username, password, access, project ) VALUES ( ?, ?, ?, ? );"; - $bind_variables = array( $this->username, $this->password, $this->access, null ); + $bind_variables = array( $username, $password, $access, null ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + $pass = false; if( $return > 0 ) { - $this->set_default_options(); - echo formatJSEND( "success", array( "username" => $this->username ) ); - } else { - - echo formatJSEND( "error", "The Username is Already Taken" ); + $this->set_default_options( $username ); + $pass = true; } + return $pass; } - public function delete_user() { + public function delete_user( $username ) { global $sql; - $query = "DELETE FROM user_options WHERE username=?;"; - $bind_variables = array( $this->username ); + $query = "DELETE FROM user_options WHERE user=( SELECT id FROM users WHERE username=? );"; + $bind_variables = array( $username ); $return = $sql->query( $query, $bind_variables, -1, "rowCount" ); if( $return > -1 ) { - $query = "DELETE FROM projects WHERE owner=? AND access IN ( ?,?,?,?,? );"; + $query = " + DELETE FROM projects + WHERE owner=( SELECT id FROM users WHERE username=? ) + AND ( + SELECT COUNT(*) + FROM access + WHERE project = projects.id + AND user <> ( SELECT id FROM users WHERE username=? ) + ) = 0;"; $bind_variables = array( - $this->username, - "null", - null, - "[]", - "", - json_encode( array( $this->username ) ) + $username, + $username ); $return = $sql->query( $query, $bind_variables, -1, "rowCount" ); if( $return > -1 ) { $query = "DELETE FROM users WHERE username=?;"; - $bind_variables = array( $this->username ); + $bind_variables = array( $username ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $return > 0 ) { - echo formatJSEND( "success", null ); + exit( formatJSEND( "success", null ) ); } else { - echo formatJSEND( "error", "Error deleting user information." ); + exit( formatJSEND( "error", "Error deleting user information." ) ); } } else { - echo formatJSEND( "error", "Error deleting user project information." ); + exit( formatJSEND( "error", "Error deleting user project information." ) ); } } else { - echo formatJSEND( "error", "Error deleting user option information." ); + exit( formatJSEND( "error", "Error deleting user option information." ) ); } } @@ -107,15 +95,8 @@ class User { global $sql; $query = "SELECT * FROM users WHERE username=?"; $bind_variables = array( $username ); - $return = $sql->query( $query, $bind_variables, array() ); - - if( ! empty( $return ) ) { - - echo formatJSEND( "success", $return ); - } else { - - echo formatJSEND( "error", "Could not select user." ); - } + $return = $sql->query( $query, $bind_variables, array(), "fetch" ); + return $return; } public function list_users() { @@ -134,26 +115,26 @@ class User { } } - public function set_default_options() { + public function set_default_options( $username ) { foreach( Settings::DEFAULT_OPTIONS as $id => $option ) { global $sql; - $query = "INSERT INTO user_options ( name, username, value ) VALUES ( ?, ?, ? );"; + $query = "INSERT INTO user_options ( name, user, value ) VALUES ( ?, ( SELECT id FROM users WHERE username=? ), ? );"; $bind_variables = array( $option["name"], - $this->username, + $username, $option["value"], ); $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $result == 0 ) { - $query = "UPDATE user_options SET value=? WHERE name=? AND username=?;"; + $query = "UPDATE user_options SET value=? WHERE name=? AND user=( SELECT id FROM users WHERE username=? );"; $bind_variables = array( $option["value"], $option["name"], - $this->username, + $username, ); $result = $sql->query( $query, $bind_variables, 0, "rowCount" ); } @@ -164,59 +145,18 @@ class User { // Authenticate ////////////////////////////////////////////////////////////////// - public function Authenticate() { + public function Authenticate( $username, $password ) { - if( $this->username == "" || $this->password == "" ) { + if( $username == "" || $password == "" ) { - exit( formatJSEND( "error", "Username or password can not be blank." ) ); - } - - if( ! is_dir( SESSIONS_PATH ) ) { - - mkdir( SESSIONS_PATH, 00755 ); - } - - $permissions = array( - "755", - "0755" - ); - - $server_user = posix_getpwuid( posix_geteuid() ); - $sessions_permissions = substr( sprintf( '%o', fileperms( SESSIONS_PATH ) ), -4 ); - $sessions_owner = posix_getpwuid( fileowner( SESSIONS_PATH ) ); - - if( is_array( $server_user ) ) { - - $server_user = $server_user["uid"]; - } - - if( ! ( $sessions_owner === $server_user ) ) { - - try { - - chown( SESSIONS_PATH, $server_user ); - } catch( Exception $e ) { - - exit( formatJSEND("error", "Error, incorrect owner of sessions folder. Expecting: $server_user, Recieved: " . $sessions_owner ) ); - } - } - - if( ! in_array( $sessions_permissions, $permissions ) ) { - - try { - - chmod( SESSIONS_PATH, 00755 ); - } catch( Exception $e ) { - - exit( formatJSEND("error", "Error, incorrect permissions on sessions folder. Expecting: 0755, Recieved: " . $sessions_permissions ) ); - } + return false; } global $sql; $pass = false; - $this->EncryptPassword(); + $password = $this->encrypt_password( $password ); $query = "SELECT * FROM users WHERE username=? AND password=?;"; - $bind_variables = array( $this->username, $this->password ); + $bind_variables = array( $username, $password ); $return = $sql->query( $query, $bind_variables, array() ); /** @@ -226,52 +166,46 @@ class User { if( ( strtolower( DBTYPE ) == "mysql" ) && empty( $return ) ) { $query = "SELECT * FROM users WHERE username=? AND password=PASSWORD( ? );"; - $bind_variables = array( $this->username, $this->password ); + $bind_variables = array( $username, $password ); $return = $sql->query( $query, $bind_variables, array() ); if( ! empty( $return ) ) { $query = "UPDATE users SET password=? WHERE username=?;"; - $bind_variables = array( $this->password, $this->username ); + $bind_variables = array( $password, $username ); $return = $sql->query( $query, $bind_variables, array() ); $query = "SELECT * FROM users WHERE username=? AND password=?;"; - $bind_variables = array( $this->username, $this->password ); + $bind_variables = array( $username, $password ); $return = $sql->query( $query, $bind_variables, array() ); } } if( ! empty( $return ) ) { + $user = $return[0]; $pass = true; $token = mb_strtoupper( strval( bin2hex( openssl_random_pseudo_bytes( 16 ) ) ) ); $_SESSION['id'] = SESSION_ID; - $_SESSION['user'] = $this->username; + $_SESSION['user'] = $username; + $_SESSION['user_id'] = $user["id"]; $_SESSION['token'] = $token; - $_SESSION['lang'] = $this->lang; - $_SESSION['theme'] = $this->theme; $_SESSION["login_session"] = true; - $user = $return[0]; $query = "UPDATE users SET token=? WHERE username=?;"; - $bind_variables = array( sha1( $token ), $this->username ); + $bind_variables = array( sha1( $token ), $username ); $return = $sql->query( $query, $bind_variables, 0, 'rowCount' ); + $projects = $sql->query( "SELECT path FROM projects WHERE id = ?", array( $user["project"] ), array() ); - if( isset( $user['project'] ) && $user['project'] != '' ) { + if( isset( $user['project'] ) && $user['project'] != '' && ! empty( $projects ) ) { - $_SESSION['project'] = $user['project']; + $_SESSION['project'] = $projects[0]["path"]; + $_SESSION['project_id'] = $user['project']; } - $this->checkDuplicateSessions( $this->username ); - } - - if( $pass ) { - - echo formatJSEND( "success", array( "username" => $this->username ) ); - } else { - - echo formatJSEND( "error", "Incorrect Username or Password" ); + $this->checkDuplicateSessions( $username ); } + return $pass; } /** @@ -353,28 +287,31 @@ class User { // Create Account ////////////////////////////////////////////////////////////////// - public function Create() { + public function Create( $username, $password ) { - $this->EncryptPassword(); - $this->add_user(); + $username = self::CleanUsername( $username ); + $password = $this->encrypt_password( $password ); + $result = $this->add_user( $username, $password, Permissions::SYSTEM_LEVELS["user"] ); + return $result; } ////////////////////////////////////////////////////////////////// // Delete Account ////////////////////////////////////////////////////////////////// - public function Delete() { + public function Delete( $username ) { - $this->delete_user(); + $username = self::CleanUsername( $username ); + return $this->delete_user( $username ); } ////////////////////////////////////////////////////////////////// // Encrypt Password ////////////////////////////////////////////////////////////////// - private function EncryptPassword() { + private function encrypt_password( $password ) { - $this->password = sha1( md5( $this->password ) ); + return sha1( md5( $password ) ); } ////////////////////////////////////////////////////////////////// @@ -384,10 +321,10 @@ class User { public function Password() { global $sql; - $this->EncryptPassword(); + $password = $this->encrypt_password( $this->password ); $query = "UPDATE users SET password=? WHERE username=?;"; - $bind_variables = array( $this->password, $this->username ); - $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); + $bind_variables = array( $password, $this->username ); + $return = $sql->query( $query, $bind_variables, 0, "rowCount", "exception" ); if( $return > 0 ) { @@ -418,19 +355,19 @@ class User { } } - public function update_access() { + public function update_access( $username, $access ) { global $sql; $query = "UPDATE users SET access=? WHERE username=?;"; - $bind_variables = array( $this->access, $this->username ); + $bind_variables = array( $access, $username ); $return = $sql->query( $query, $bind_variables, 0, "rowCount" ); if( $return > 0 ) { - echo formatJSEND( "success", "Updated access for {$this->username}" ); + echo formatJSEND( "success", "Updated access for {$username}" ); } else { - echo formatJSEND( "error", "Error updating project" ); + echo formatJSEND( "error", "Error updating access" ); } } diff --git a/components/user/controller.php b/components/user/controller.php index 266d3a6..ab79c0f 100755 --- a/components/user/controller.php +++ b/components/user/controller.php @@ -1,164 +1,250 @@ username = User::CleanUsername( $_POST['username'] ); - $User->password = $_POST['password']; - - // check if the asked languages exist and is registered in languages/code.php - require_once '../../languages/code.php'; - if (isset($languages[ $_POST['language'] ])) { - $User->lang = $_POST['language']; - } else { - $User->lang = 'en'; - } - - // theme - $User->theme = $_POST['theme']; - - $User->Authenticate(); +if( $_GET['action'] != 'authenticate' ) { + + checkSession(); } - ////////////////////////////////////////////////////////////////// - // Logout - ////////////////////////////////////////////////////////////////// +$User = new User(); -if ($_GET['action']=='logout') { +////////////////////////////////////////////////////////////////// +// Authenticate +////////////////////////////////////////////////////////////////// + +if($_GET['action']=='authenticate') { + + if( ! isset( $_POST['username'] ) || ! isset( $_POST['password'] ) ) { + + die( formatJSEND( "error", "Missing username or password" ) ); + } + + $username = User::CleanUsername( $_POST['username'] ); + $password = $_POST['password']; + + // check if the asked languages exist and is registered in languages/code.php + require_once '../../languages/code.php'; + if( isset( $languages[$_POST['language']] ) ) { + + $lang = $_POST['language']; + } else { + + $lang = 'en'; + } + + // theme + $theme = $_POST['theme']; + $permissions = array( + "755", + "0755" + ); + + if( ! is_dir( SESSIONS_PATH ) ) { + + mkdir( SESSIONS_PATH, 00755 ); + } + + $server_user = getmyuid(); + $sessions_permissions = substr( sprintf( '%o', fileperms( SESSIONS_PATH ) ), -4 ); + $sessions_owner = fileowner( SESSIONS_PATH ); + + if( is_array( $server_user ) ) { + + $server_user = $server_user["uid"]; + } + + if( ! ( $sessions_owner === $server_user ) ) { + + try { + + chown( SESSIONS_PATH, $server_user ); + } catch( Exception $e ) { + + exit( formatJSEND("error", "Error, incorrect owner of sessions folder. Expecting: $server_user, Recieved: " . $sessions_owner ) ); + } + } + + if( ! in_array( $sessions_permissions, $permissions ) ) { + + try { + + chmod( SESSIONS_PATH, 00755 ); + } catch( Exception $e ) { + + exit( formatJSEND("error", "Error, incorrect permissions on sessions folder. Expecting: 0755, Recieved: " . $sessions_permissions ) ); + } + } + + $pass = $User->Authenticate( $username, $password ); + + if( $pass ) { + + $_SESSION['lang'] = $lang; + $_SESSION['theme'] = $theme; + exit( formatJSEND( "success", array( "username" => $username ) ) ); + } else { + + exit( formatJSEND( "error", "Incorrect Username or Password" ) ); + } +} + +////////////////////////////////////////////////////////////////// +// Logout +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'logout' ) { logout(); } - ////////////////////////////////////////////////////////////////// - // Create User - ////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// Create User +////////////////////////////////////////////////////////////////// -if ($_GET['action']=='create') { - if (checkAccess()) { - if (!isset($_POST['username']) || !isset($_POST['password'])) { - die(formatJSEND("error", "Missing username or password")); - } - - $User->username = User::CleanUsername( $_POST['username'] ); - $User->password = $_POST['password']; - $User->Create(); - } -} - - ////////////////////////////////////////////////////////////////// - // Delete User - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='delete') { - if (checkAccess()) { - if (!isset($_GET['username'])) { - die(formatJSEND("error", "Missing username")); - } - - $User->username = User::CleanUsername( $_GET['username'] ); - $User->Delete(); - } -} - - ////////////////////////////////////////////////////////////////// - // Change Password - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='password') { - if (!isset($_POST['username']) || !isset($_POST['password'])) { - die(formatJSEND("error", "Missing username or password")); - } - - if (checkAccess() || $_POST['username'] == $_SESSION['user']) { - $User->username = User::CleanUsername( $_POST['username'] ); - $User->password = $_POST['password']; - $User->Password(); - } -} - - ////////////////////////////////////////////////////////////////// - // Change Project - ////////////////////////////////////////////////////////////////// - -if ($_GET['action']=='project') { - if (!isset($_GET['project'])) { - die(formatJSEND("error", "Missing project")); - } - - $User->username = $_SESSION['user']; - $User->project = $_GET['project']; - $User->Project(); -} - - ////////////////////////////////////////////////////////////////// - // Search Users - ////////////////////////////////////////////////////////////////// - -if ( $_GET['action'] == 'search_users' ) { +if( $_GET['action'] == 'create' ) { - if ( ! isset( $_GET['search_term'] ) ) { + if( checkAccess() ) { + + if ( ! isset( $_POST['username'] ) || ! isset( $_POST['password'] ) ) { + + exit( formatJSEND( "error", "Missing username or password" ) ); + } + + if ( ! ( $_POST['password'] === $_POST['password2'] ) ) { + + exit( formatJSEND( "error", "Passwords do not match" ) ); + } + + if ( preg_match( '/[^\w\-\._@]/', $_POST['username'] ) ) { + + exit( formatJSEND( "error", "Invalid characters in username" ) ); + } + + $result = $User->Create( $_POST['username'], $_POST['password'] ); + + if( $result ) { + + exit( formatJSEND( "success", "User successfully created." ) ); + } else { + + exit( formatJSEND( "error", "User could not be created." ) ); + } + } +} + +////////////////////////////////////////////////////////////////// +// Delete User +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'delete' ) { + + if( checkAccess() ) { + + if( ! isset( $_GET['username'] ) ) { + + exit( formatJSEND( "error", "Missing username" ) ); + } + + $return = $User->Delete( $_GET['username'] ); + exit( json_encode( $return ) ); + } +} + +////////////////////////////////////////////////////////////////// +// Change Password +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'password' ) { + + if( ! isset( $_POST['username']) || ! isset( $_POST['password'] ) ) { + + die( formatJSEND( "error", "Missing username or password" ) ); + } + + if( $_POST['username'] == $_SESSION['user'] || is_admin() ) { + + $User->username = User::CleanUsername( $_POST['username'] ); + $User->password = $_POST['password']; + $User->Password(); + } +} + +////////////////////////////////////////////////////////////////// +// Change Project +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'project' ) { + + if( ! isset( $_GET['project'] ) ) { + + die( formatJSEND( "error", "Missing project" ) ); + } + + $User->username = $_SESSION['user']; + $User->project = $_GET['project']; + $User->Project(); +} + +////////////////////////////////////////////////////////////////// +// Search Users +////////////////////////////////////////////////////////////////// + +if( $_GET['action'] == 'search_users' ) { + + if( ! isset( $_GET['search_term'] ) ) { die( formatJSEND( "error", "Missing search term" ) ); } + search_users( $_GET['search_term'], "exit", true ); } - ////////////////////////////////////////////////////////////////// - // Verify User Account - ////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////// +// Verify User Account +////////////////////////////////////////////////////////////////// -if ($_GET['action']=='verify') { - - $User->username = $_SESSION['user']; - //$User->Verify(); - checkSession(); +if( $_GET['action'] == 'verify' ) { + + $User->username = $_SESSION['user']; + checkSession(); } -if ( $_GET['action'] == 'update_access' ) { +if( $_GET['action'] == 'update_access' ) { checkSession(); - if ( ! isset( $_GET['access'] ) || ! isset( $_GET['username'] ) ) { + if( ! isset( $_POST['access'] ) || ! isset( $_POST['user'] ) ) { die( formatJSEND( "error", "Could not update access." ) ); } if( ! is_admin() ) { - die( formatJSEND( "error", "You do not have permission to update access." ) ); + die( formatJSEND( "error", "You do not have permission to update user's access." ) ); } - $User->username = $_GET["username"]; - $User->access = $_GET["access"]; - $User->update_access(); + if( ! in_array( $_POST["access"], array_keys( Permissions::SYSTEM_LEVELS ) ) ) { + + exit( formatJSEND( "error", "Invalid access level specified." ) ); + } + + $User->update_access( $_POST["user"], Permissions::SYSTEM_LEVELS["{$_POST["access"]}"] ); } diff --git a/components/user/dialog.php b/components/user/dialog.php index 296ed3f..aad1e97 100755 --- a/components/user/dialog.php +++ b/components/user/dialog.php @@ -72,10 +72,10 @@ switch($_GET['action']){
    -

    +

    - + +
    - - - - -
    - - - - -
    - - - - - - - - - - ' ); + } else { + + echo( '' ); + } } - - ?> - -
    - - - -
    : 0 · : 0
    - -
    -
    -
    -
    - -
    -
    - "> -
    - -
    -
    - -
    - -
    - - -
    '); - echo(''.$bottommenu['title'].''); - } - } - } - } - } - } - - ?> - -
    - -
    - -
    -
    -
    -
    -
    - -
    - - - - - - - - - - -
      - - - - - - - - - - - - "'); - } - } - - foreach($plugins as $plugin){ - if(file_exists(PLUGINS . "/" . $plugin . "/init.js")){ - echo('"'); - } - } - - } - - ?> - + + // Load Component CSS Files + foreach( $components as $component ) { + + if( file_exists( THEMES . "/". $theme . "/" . $component . "/screen.css" ) ) { + + echo(''); + } else { + + if( file_exists( "themes/default/" . $component . "/screen.css" ) ) { + + echo( '' ); + } else { + + if( file_exists( COMPONENTS . "/" . $component . "/screen.css" ) ) { + + echo( '' ); + } + } + } + } + + // Load Plugin CSS Files + foreach( $plugins as $plugin ) { + + if( file_exists( THEMES . "/". $theme . "/" . $plugin . "/screen.css" ) ) { + + echo( '' ); + } else { + + if( file_exists( "themes/default/" . $plugin . "/screen.css" ) ) { + + echo( '' ); + } else { + + if( file_exists( PLUGINS . "/" . $plugin . "/screen.css" ) ) { + + echo( '' ); + } + } + } + } + ?> + + + + + + + + + + + + + + + + + + + +
      + +
      + + + +
      + + + + +
      + + +
      + + + +
      +
      Drop files or folders here.
      + +
      +
      +

      + +
      +
      +
      +
      +
      + : 0 · : 0 +
      +
      +
      +
        +
        + +
        +
        + "> +
        + +
        +
        +
        +
        + + + +
        ' ); + echo( '' . $bottommenu['title'] . '' ); + } + } + } + } + } + } + ?> +
        + +
        + + + +
        +
        +
        +
        + +
        + + + + + +
        +
          +
          + + + + + + + + + + + + "' ); + } + } + + foreach( $plugins as $plugin ) { + + if( file_exists( PLUGINS . "/" . $plugin . "/init.js" ) ) { + + echo( '"' ); + } + } + } + ?> + diff --git a/js/jsend.js b/js/jsend.js index 256778a..60370e4 100755 --- a/js/jsend.js +++ b/js/jsend.js @@ -1,6 +1,6 @@ ( function( global, $ ) { - var codiad = global.codiad; + let codiad = global.codiad; ////////////////////////////////////////////////////////////////////// // Parse JSEND Formatted Returns @@ -11,7 +11,8 @@ parse: function( d ) { // (Data) - var obj = $.parseJSON( d ); + let obj = $.parseJSON( d ); + if ( obj === undefined || obj === null ) { return 'error'; diff --git a/js/modal.js b/js/modal.js index 6147d08..9961760 100755 --- a/js/modal.js +++ b/js/modal.js @@ -21,9 +21,13 @@ load: function( width, url, data ) { - data = data || {}; - var bounds = this._getBounds( width ); - $('#modal') + return new Promise( function( resolve, reject ) { + + data = data || {}; + let _this = codiad.modal; + let bounds = _this._getBounds( width ); + let content = $( '#modal-content' ) + $('#modal') .css({ 'top': bounds.top, 'left': bounds.left, @@ -33,23 +37,26 @@ .draggable({ handle: '#drag-handle' }); - $('#modal-content') - .html(''); - this.load_process = $.get( url, data, function( data ) { - $('#modal-content').html( data ); - // Fix for Firefox autofocus goofiness - $('input[autofocus="autofocus"]') - .focus(); - }); - var event = {animationPerformed: false}; - amplify.publish( 'modal.onLoad', event ); - // If no plugin has provided a custom load animation - if( ! event.animationPerformed ) { + content.html(''); - $('#modal, #modal-overlay') - .fadeIn(200); - } - codiad.sidebars.modalLock = true; + _this.load_process = $.get( url, data, function( data ) { + + content.html( data ); + // Fix for Firefox autofocus goofiness + $('#modal-content input[autofocus="autofocus"]').focus(); + resolve( content ); + }).error( reject ); + + 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() { diff --git a/js/system.js b/js/system.js index 0907b07..e786ecd 100755 --- a/js/system.js +++ b/js/system.js @@ -126,7 +126,7 @@ create_default_tables: function() { jQuery.ajax({ - + url: this.controller, type: "POST", dataType: 'html', @@ -135,15 +135,6 @@ }, success: function( data ) { - let response = codiad.jsend.parse( data ); - - if( response.status != 'error' ) { - - codiad.message.success( i18n( 'Created Default Tables' ) ); - } else { - - codiad.message.error( i18n( 'Error Creating Default Tables' ) ); - } console.log( data ); }, error: function(jqXHR, textStatus, errorThrown) { @@ -158,6 +149,45 @@ }, }); }, + + get_ini_setting: async function( option ) { + + let i = null; + let result = await jQuery.ajax({ + + url: this.controller, + type: "POST", + dataType: 'html', + data: { + action: 'get_ini_setting', + option: option, + }, + success: function( data ) { + + console.log( data ); + }, + error: function(jqXHR, textStatus, errorThrown) { + + codiad.message.error( i18n( 'Error Creating Default Tables' ) ); + console.log('jqXHR:'); + console.log(jqXHR); + console.log('textStatus:'); + console.log(textStatus); + console.log('errorThrown:'); + console.log(errorThrown); + }, + }); + + try { + + let data = JSON.parse( result ); + i = data.value; + } catch( e ) { + + console.log( e, result ); + } + return i; + } }; })(this, jQuery); diff --git a/themes/default/filemanager/screen.css b/themes/default/filemanager/screen.css index 2864f74..ed12709 100755 --- a/themes/default/filemanager/screen.css +++ b/themes/default/filemanager/screen.css @@ -1,4 +1,4 @@ -#file-manager { +.file-manager { width: 100%; height: 100%; float: left; @@ -6,32 +6,32 @@ padding: 15px 15px 30px 15px; overflow: auto; } -#file-manager ul { +.file-manager ul { display: block; margin: 0; padding: 0; } -#file-manager li { +.file-manager li { display: block; margin: 0; padding: 2px 0; list-style: none; } -#file-manager ul ul li { +.file-manager ul ul li { margin-left: 20px; white-space: nowrap; } -#file-manager a { +.file-manager a { display: inline-block; min-width: 100%; cursor: pointer; padding: 5px; border-radius: 3px; } -#file-manager a:hover, #file-manager a.context-menu-active { +.file-manager a:hover, .file-manager a.context-menu-active { background-color: #333; } -#file-manager span { +.file-manager span { width:10px; height:10px; display: inline-block; @@ -39,43 +39,51 @@ padding-top: 0; line-height: 6px; } -#file-manager .plus:before { +.file-manager .plus:before { display: block; content: "\25b8"; padding: 5px; margin-left: -10px; font-size: 16px; } -#file-manager .plus { +.file-manager .plus { color: gray; font-family: entypo; font-style: normal; display: inline-block; } -#file-manager .none { +.file-manager .none { background: none; } -#file-manager .minus:before { +.file-manager .minus:before { display: block; content: "\25be"; padding: 5px; margin-left: -10px; font-size: 16px; } -#file-manager .minus { +.file-manager .minus { color: gray; font-family: entypo; font-style: normal; display: inline-block; } -#file-manager .plus:hover { +.file-manager .plus:hover { cursor: pointer; color: #fff; } -#file-manager .minus:hover { +.file-manager .minus:hover { cursor: pointer; color: #fff; } +.file-manager-selected { + + background-color: #4a4a4a; +} +.file-manager-selected:hover { + + background-color: #4a4a4a !important; +} /* CONTEXT MENU */ #context-menu { @@ -143,66 +151,6 @@ float: right; background: url(../loading.gif) no-repeat; } -/* UPLOADER */ - -#upload-drop-zone { - height: 200px; - padding-top: 60px; - border: 1px solid #666; - border-radius: 5px; - margin: 10px 0; - box-shadow: inset 0px 0px 10px 0px rgba(0, 0, 0, .5); - text-align: center; - font-size: 25px; - color: #999; -} -.upload-drag-over { - border: 1px solid #fff; -} -#upload-wrapper { - cursor: pointer; - display: inline-block; - overflow: hidden; - position: relative; -} -#upload-clicker { - background: #333; - color: #999; - cursor: pointer; - display: inline-block; -} -#upload-wrapper:hover #upload-clicker { - color: #fff; -} -#upload-wrapper input { - cursor: pointer; - height: 100%; - position: absolute; - right: 0; - top: 0; - opacity: 0.0; -} -#upload-progress { - width: 75%; - margin: 10px auto; - height: 20px; - border: 1px solid #666; - background: #262626; - border-radius: 5px; - overflow: hidden; -} -#upload-progress .bar { - width: 0; - height: 20px; - background-image: url(images/progress_bar.png); -} -#upload-complete { - display: none; - font-size: 18px; - margin: 10px 0; - color: #fff; -} - /* SEARCH */ @@ -214,210 +162,220 @@ /* ICONS */ -#file-manager a { +.file-manager a { background-repeat: no-repeat; background-position: 4px 4px; text-indent: 22px; padding-top: 5px; } -#file-manager .directory { +.file-manager .directory { background-image: url(images/directory.png); } -#file-manager .directory.open { +.file-manager .directory.open { background-image: url(images/directory_open.png); } -#file-manager .file { +.file-manager .file { background-image: url(images/file.png); } /* EXTENSIONS */ -#file-manager .ext-htaccess { +.file-manager .ext-htaccess { background-image: url(images/config.png); } -#file-manager .ext-conf { +.file-manager .ext-conf { background-image: url(images/config.png); } -#file-manager .ext-ini { +.file-manager .ext-ini { background-image: url(images/config.png); } -#file-manager .ext-3gp { +.file-manager .ext-3gp { background-image: url(images/film.png); } -#file-manager .ext-afp { +.file-manager .ext-afp { background-image: url(images/code.png); } -#file-manager .ext-afpa { +.file-manager .ext-afpa { background-image: url(images/code.png); } -#file-manager .ext-asp { +.file-manager .ext-asp { background-image: url(images/code.png); } -#file-manager .ext-aspx { +.file-manager .ext-aspx { background-image: url(images/code.png); } -#file-manager .ext-avi { +.file-manager .ext-avi { background-image: url(images/film.png); } -#file-manager .ext-bat { +.file-manager .ext-bat { background-image: url(images/application.png); } -#file-manager .ext-bmp { +.file-manager .ext-bmp { background-image: url(images/picture.png); } -#file-manager .ext-c { +.file-manager .ext-c { background-image: url(images/code.png); } -#file-manager .ext-cfm { +.file-manager .ext-cfm { background-image: url(images/code.png); } -#file-manager .ext-cgi { +.file-manager .ext-cgi { background-image: url(images/code.png); } -#file-manager .ext-com { +.file-manager .ext-com { background-image: url(images/application.png); } -#file-manager .ext-cpp { +.file-manager .ext-cpp { background-image: url(images/code.png); } -#file-manager .ext-css { +.file-manager .ext-css { background-image: url(images/css.png); } -#file-manager .ext-doc { +.file-manager .ext-doc { background-image: url(images/doc.png); } -#file-manager .ext-exe { +.file-manager .ext-exe { background-image: url(images/application.png); } -#file-manager .ext-gif { +.file-manager .ext-gif { background-image: url(images/picture.png); } -#file-manager .ext-fla { +.file-manager .ext-fla { background-image: url(images/flash.png); } -#file-manager .ext-h { +.file-manager .ext-h { background-image: url(images/code.png); } -#file-manager .ext-htm { +.file-manager .ext-htm { background-image: url(images/html.png); } -#file-manager .ext-html { +.file-manager .ext-html { background-image: url(images/html.png); } -#file-manager .ext-jar { +.file-manager .ext-jar { background-image: url(images/java.png); } -#file-manager .ext-jpg { +.file-manager .ext-jpg { background-image: url(images/picture.png); } -#file-manager .ext-jpeg { +.file-manager .ext-jpeg { background-image: url(images/picture.png); } -#file-manager .ext-js { +.file-manager .ext-js { background-image: url(images/script.png); } -#file-manager .ext-json { +.file-manager .ext-json { background-image: url(images/script.png); } -#file-manager .ext-lasso { +.file-manager .ext-lasso { background-image: url(images/code.png); } -#file-manager .ext-log { +.file-manager .ext-log { background-image: url(images/text-plain.png); } -#file-manager .ext-m4p { +.file-manager .ext-m4p { background-image: url(images/music.png); } -#file-manager .ext-mov { +.file-manager .ext-mov { background-image: url(images/film.png); } -#file-manager .ext-mp3 { +.file-manager .ext-mp3 { background-image: url(images/music.png); } -#file-manager .ext-mp4 { +.file-manager .ext-mp4 { background-image: url(images/film.png); } -#file-manager .ext-mpg { +.file-manager .ext-mpg { background-image: url(images/film.png); } -#file-manager .ext-mpeg { +.file-manager .ext-mpeg { background-image: url(images/film.png); } -#file-manager .ext-ogg { +.file-manager .ext-ogg { background-image: url(images/music.png); } -#file-manager .ext-pcx { +.file-manager .ext-pcx { background-image: url(images/picture.png); } -#file-manager .ext-pdf { +.file-manager .ext-pdf { background-image: url(images/pdf.png); } -#file-manager .ext-php { +.file-manager .ext-php { background-image: url(images/php.png); } -#file-manager .ext-png { +.file-manager .ext-png { background-image: url(images/picture.png); } -#file-manager .ext-ppt { +.file-manager .ext-ppt { background-image: url(images/ppt.png); } -#file-manager .ext-psd { +.file-manager .ext-psd { background-image: url(images/psd.png); } -#file-manager .ext-pl { +.file-manager .ext-pl { background-image: url(images/script.png); } -#file-manager .ext-py { +.file-manager .ext-py { background-image: url(images/script.png); } -#file-manager .ext-rb { +.file-manager .ext-rb { background-image: url(images/ruby.png); } -#file-manager .ext-rbx { +.file-manager .ext-rbx { background-image: url(images/ruby.png); } -#file-manager .ext-rhtml { +.file-manager .ext-rhtml { background-image: url(images/ruby.png); } -#file-manager .ext-rpm { +.file-manager .ext-rpm { background-image: url(images/linux.png); } -#file-manager .ext-ruby { +.file-manager .ext-ruby { background-image: url(images/ruby.png); } -#file-manager .ext-sql { +.file-manager .ext-sql { background-image: url(images/db.png); } -#file-manager .ext-swf { +.file-manager .ext-swf { background-image: url(images/flash.png); } -#file-manager .ext-tif { +.file-manager .ext-tif { background-image: url(images/picture.png); } -#file-manager .ext-tiff { +.file-manager .ext-tiff { background-image: url(images/picture.png); } -#file-manager .ext-txt { +.file-manager .ext-txt { background-image: url(images/file.png); } -#file-manager .ext-vb { +.file-manager .ext-vb { background-image: url(images/code.png); } -#file-manager .ext-wav { +.file-manager .ext-wav { background-image: url(images/music.png); } -#file-manager .ext-wmv { +.file-manager .ext-wmv { background-image: url(images/film.png); } -#file-manager .ext-xls { +.file-manager .ext-xls { background-image: url(images/xls.png); } -#file-manager .ext-xml { +.file-manager .ext-xml { background-image: url(images/code.png); } -#file-manager .ext-zip { +.file-manager .ext-zip { background-image: url(images/zip.png); } -#file-manager .loading { +.file-manager .loading { background-image: url(images/spinner.gif); } + +.file-manager .drag_start { + background-color: #333; + /*border-radius: 10px;*/ +} +.file-manager .drag_over { + background-color: #4a4a4a; + border-radius: 10px; +} + diff --git a/themes/default/screen.css b/themes/default/screen.css index ee6e2c4..99eb401 100755 --- a/themes/default/screen.css +++ b/themes/default/screen.css @@ -20,7 +20,7 @@ background-color: #999; } -::-webkit-scrollbar-thumb:horizontal {i +::-webkit-scrollbar-thumb:horizontal { width: 5px; background-color: #666; -webkit-border-radius: 3px; @@ -467,6 +467,100 @@ table [class^="icon-"], [class*=" icon-"] { background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0.29, rgb(120,120,120)),color-stop(0.77, rgb(77,77,77))); cursor: row-resize; } + +#uploads-button { + + bottom: 4px; + color: #999; + font-size: 12px; + position: absolute; + right: 100px; + z-index: 2; +} + +.uploads-container { + + background: #1a1a1a; + bottom: 25px; + display: none; + height: 50%; + overflow-y: auto; + position: absolute; + right: 100px; + width: 250px; + z-index: 2; +} + +.uploads-container .project-list-title { + + position: unset; + margin-bottom: 5px; +} + +.uploads-content { + + height: 100%; + text-align: center; + width: 100%; +} + +.upload-container { + + display: inline-block; + height: auto; + width: 95%; +} + +.upload-title { + + display: inline-block; + max-width: 75%; + width: 80%; +} + +.upload-progress-text { + + display: inline-block; + width: 20%; +} + +.upload-progress { + + width: 75%; + margin: 10px auto; + height: 20px; + border: 1px solid #666; + background: #262626; + border-radius: 5px; + overflow: hidden; +} + +.upload-progress .bar { + + width: 0; + height: 20px; + background-image: url( filemanager/images/progress_bar.png ); +} + +.upload-dismiss { + + display: inline-block; + left: -45px; + max-width: 75%; + position: relative; + text-decoration: underline; + width: auto; +} + +.upload-cancel { + + color: red; + display: inline-block; + position: relative; + right: -45px; + width: auto; +} + #cursor-position { position: absolute; right: 30px; @@ -609,6 +703,7 @@ table [class^="icon-"], [class*=" icon-"] { left: 0; right: 10px; bottom: 276px; + background-color: #1a1a1a; } .sb-left-projects { @@ -816,6 +911,7 @@ table [class^="icon-"], [class*=" icon-"] { background: url(loading.gif) no-repeat center; } + /* Download iFrame */ #download { display: none; @@ -828,4 +924,30 @@ table [class^="icon-"], [class*=" icon-"] { height: 25px; vertical-align: middle; width: 25px; -} \ No newline at end of file +} + +.drop-overlay { + + display: none; + + border: 2px dashed #fff; + border-radius: 10px; + + background: rgba( 255, 255, 255, 0.15 ); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: 97; +} + +.drop-overlay-message { + + text-align: center; + position: relative; + float: left; + top: 50%; + left: 50%; + transform: translate( -50%, -50% ); +}