Browse Source

19-09-2019 update

+ fixDesktop filename script for window
+ Added more icon and images
+ Change to USB mount script
+ Change in shutdown GUI
+ more
Toby Chui 6 years ago
parent
commit
2f39722a7e

+ 60 - 0
src/Desktop/fixWinDesktop.php

@@ -0,0 +1,60 @@
+<?php
+/*
+Desktop filename decode error auto fixing script
+*/
+function gen_uuid() {
+    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+        // 32 bits for "time_low"
+        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+
+        // 16 bits for "time_mid"
+        mt_rand( 0, 0xffff ),
+
+        // 16 bits for "time_hi_and_version",
+        // four most significant bits holds version number 4
+        mt_rand( 0, 0x0fff ) | 0x4000,
+
+        // 16 bits, 8 bits for "clk_seq_hi_res",
+        // 8 bits for "clk_seq_low",
+        // two most significant bits holds zero and one for variant DCE1.1
+        mt_rand( 0, 0x3fff ) | 0x8000,
+
+        // 48 bits for "node"
+        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+    );
+}
+
+include_once("../auth.php");
+include_once("../SystemAOB/functions/personalization/configIO.php");
+$username = $_SESSION['login'];
+$desktopFolder = "files/" . $username . "/";
+if (file_exists($desktopFolder)){
+	if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+		//Window Host
+		//There might be problems when translating UTF-8 encoded filename into local encoding filenames. This might lead to load fail of the desktop icons.
+		$desktopFiles = glob($desktopFolder . "*");
+		$configs = getConfig("encoding",true);
+		$counter = 0;
+		foreach ($desktopFiles as $file){
+			//Find the file that do not fits the current local encoding environment set by the system setting
+			$conFilename = mb_convert_encoding($file, "UTF-8",$configs["forceEncodingType"][3]);
+			if ($conFilename != $file){
+				echo $conFilename;
+				if (!file_exists($desktopFolder. "recovery/")){
+					mkdir($desktopFolder . "recovery/");
+				}
+				rename($file, $desktopFolder . "recovery/" . gen_uuid() . ".zip");
+				$counter++;
+			}
+		}
+		if ($counter > 0){
+			echo "DONE";
+		}else{
+			echo "ERROR. Unable to fix the issue.";
+		}
+		
+	}else{
+		//Linux
+	}
+}
+?>

+ 42 - 2
src/Desktop/index.php

@@ -359,10 +359,11 @@ function updateDesktopFiles(){
 	$.get( "desktopFileLoader.php?username=" + username, function(data) {
 		if (Array.isArray(data) == false && data.substring(0, 5) == "ERROR"){
 			showNotification("<i class='help icon'></i> Desktop file Synchronization");
+		}else if (data == ""){
+			//PHP returns nothing, mostly due to Windows Filename encoding error.
 		}else{
 			returnedFileList = returnedFileList.concat(data[0]);
 		}
-		
 		if (arraysEqual(desktopFiles,returnedFileList)){
 			//All the files are the same as before, keep the current desktop file list
 		}else{
@@ -384,6 +385,45 @@ function initUserDesktop(callback = null,callbackvar = "",callbackSeq = 0){
 	desktopFileLocations = [];
 	desktopEmptyPositions = [];
 	//showNotification("<i class='loading spinner icon'></i> Desktop environment initializing...");
+	$.ajax({
+		url: "desktopFileLoader.php?username=" + username,
+		success: function(data){
+				if (Array.isArray(data) == false && data.substring(0, 5) == "ERROR"){
+					showNotification(data);
+				}else{
+					desktopFiles = desktopFiles.concat(data[0]); //All the raw filenames 
+					desktopFileNames = desktopFileNames.concat(data[1]); //All decoded filenames
+					desktopFileLocations = desktopFileLocations.concat(data[2]); //All desktop file locations
+					calculateEmptyDesktopSlots();
+					if (callbackSeq == 0){
+						updateDesktopGraphic();
+					}
+				}
+				if (callback != null){
+					if (callbackvar == ""){
+						callback();
+					}else{
+						callback(callbackvar);
+					}
+				}
+				if (callbackSeq == 1){
+					updateDesktopGraphic();
+				}
+			},
+		timeout: 10000,
+		error: function(jqXHR, textStatus, errorThrown) {
+				parent.msgbox("Your desktop is unable to load due to host system encoding issue. We are trying to fix the issue.","<i class='caution sign icon'></i> Critial Error!",undefined,false);
+				$.get("fixWinDesktop.php",function(data){
+					if (data.includes("ERROR") == false){
+						parent.msgbox("Your desktop is fixed and the corrupted file is moved to recover/ folder on your desktop. Click the link below to refresh.","<i class='checkmark sign icon'></i> Recovery Completed","{reload}",false);
+					}else{
+						alert("Unable to recover. See console.log for more information.");
+					}
+				});
+			}
+		}
+	   );
+	/*
 	$.get( "desktopFileLoader.php?username=" + username, function(data) {
 		if (Array.isArray(data) == false && data.substring(0, 5) == "ERROR"){
 			showNotification(data);
@@ -407,7 +447,7 @@ function initUserDesktop(callback = null,callbackvar = "",callbackSeq = 0){
 			updateDesktopGraphic();
 		}
 	});
-	
+	*/
 	$.get( "loadAllModule.php", function( data ) {
 		openWithModuleList = data;
 		

+ 2 - 1
src/System Settings/menus/file.csv

@@ -1,5 +1,6 @@
 file outline,File & Storage
 fsearch,File Search,SystemAOB/functions/system_statistic/searchFile.php
-fshortcut,Explorer Shortcuts,SystemAOB/functions/file_system/fileShortcutUI.php
+diskmg,Disk Manager,SystemAOB/system/diskmg/index.php
 usbdev,USB Mounting,SystemAOB/functions/usbMount.php
+fshortcut,Explorer Shortcuts,SystemAOB/functions/file_system/fileShortcutUI.php
 smbconf,Samba Config,SystemAOB/functions/samba_config/index.php

+ 0 - 0
src/SystemAOB/functions/file_system/developer.mode


+ 4 - 3
src/SystemAOB/functions/file_system/filesUploadHandler.php

@@ -7,10 +7,10 @@ include '../../../auth.php';
 if (isset($_FILES['files']) && !empty($_FILES['files'])) {
 	if (isset($_GET['path']) && $_GET['path'] != ""){
 		$path = $_GET['path'];
-		if  (strpos($path,"/SystemAOB") === false && $path != "/media"){
+		if  ((strpos($path,"/SystemAOB") === false || file_exists("developer.mode")) && $path != "/media"){
 			$path = $path . "/";
-			if (strpos($path,"AOB/") !== false){
-				$path = str_replace("AOB/","../../../",$path);
+			if (strpos($path,"AOB/") === 0){
+				$path = "../../../" . substr($path,4);
 			}
 			$no_files = count($_FILES["files"]['name']);
 			for ($i = 0; $i < $no_files; $i++) {
@@ -49,6 +49,7 @@ if (isset($_FILES['files']) && !empty($_FILES['files'])) {
 				die("ERROR. /media is mounting directory.");
 			}else{
 				die("ERROR. SystemAOB is not a valid upload path for files.");
+		
 			}
 			
 		}

+ 1 - 1
src/SystemAOB/functions/info/version.inf

@@ -1 +1 @@
-AOB-DEVB_v6-9-2019
+AOB-DEVB_v19-9-2019

+ 1 - 0
src/SystemAOB/functions/personalization/sysconf/fsaccess.config

@@ -0,0 +1 @@
+{"syspaths":["System Paths","Allowed system paths and mount points. Seperate each path with ;","html","\/media;\/var\/www"]}

+ 19 - 14
src/SystemAOB/functions/power/shutdown-gui_2053da6fb9aa9b7605555647ee7086b181dc90b23b05c7f044c8a2fcfe933af1.php

@@ -5,23 +5,28 @@ include '../../../auth.php';
 <head>
 <title>Shutdown Sequence</title>
 </head>
-<body style="background-color:black;color:white;">
-The shutdown sequence has been running in the background.<br>
-Please wait until your ArOZ Portable System completed the shutdown sequence before you switching off the main power supply.<br>
-<br>
-You can observe the finishing of shutdown sequence of the board if you are using:<br>
-Raspberry Pi (2 / 3 / 3B+) --> Green LED is no longer flashing <br>
-Raspberry Pi Zero / Zero W --> Red Power LED is turned off<br>
-Banana Pi M2-Zero --> Red Power LED is turned on<br>
-<br>
-If you are using other development board for the ArOZ Online Portable build,<br>
-Please refer to the documentation of your board for shutdown complete indication.<br><br>
-Progress: <br>
+<body style="background-color:black;color:white;" align="center">
+    <br><br><br><br><br><br>
+    <div style="width:100%;color:#eb8934;" id="shutingDown" align="center">
+        <div style="width:100%;font-size:200%;" >䷄ Your host device is shutting down.</div>
+        <div>Do not unplug your host until the process finish.</div>
+    </div>
+    <div style="width:100%;color:#eb8934;display:none;" id="shutdownFinish" align="center">
+        <div style="width:100%;font-size:200%;" >✔ It is now safe to turn off your host device.</div><br>
+        <div>If you are running the system on SBCs, you will need to unplug the board and power it up again for restart.<br>
+        Or otherwise, assume your system is running on modern PC hardware, the system will poweroff itself.</div>
+    </div>
+<script>
+    setTimeout(function(){
+        document.getElementById("shutingDown").style.display = 'none';
+        document.getElementById('shutdownFinish').style.display = 'block';
+        
+    },10000);
+</script>
 </body>
 </html>
 
 <?php
-echo "Initiated Shutdown Sequence <br>";
-system('sudo shutdown -t 0');
+system('sudo poweroff');
 echo "DONE";
 ?>

+ 1 - 1
src/SystemAOB/functions/system_statistic/WIN32_USBlist.txt

@@ -1 +1 @@
-Generic USB Hub,Standard Enhanced PCI to USB Host Controller,Realtek RTL8192CU Wireless LAN 802.11n USB 2.0 Network Adapter,Generic USB Hub,Standard Enhanced PCI to USB Host Controller,Intel(R) USB 3.0 可延伸主機控制器,USB Composite Device,USB Root Hub,USB Root Hub,Intel(R) USB 3.0 根集線器,
+Realtek RTL8192CU Wireless LAN 802.11n USB 2.0 Network Adapter,Standard Enhanced PCI to USB Host Controller,Generic USB Hub,Standard Enhanced PCI to USB Host Controller,USB Composite Device,Intel(R) USB 3.0 可延伸主機控制器,USB Root Hub,USB Root Hub,Intel(R) USB 3.0 根集線器,Generic USB Hub,

+ 11 - 2
src/SystemAOB/functions/system_statistic/getDriveStat.php

@@ -20,7 +20,12 @@ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
            $n = "[Drive not ready]"; 
        } 
 	//echo "Drive " . $dO->DriveLetter . ": - " . $type[$dO->DriveType] . " - " . $n . " - " . $s . "<br>";
-	array_push($result,[$dO->DriveLetter . ":",$n,$s]);
+	if (isset($_GET["nomerge"])){
+	    array_push($result,[$dO->DriveLetter . ":",$n,file_size($dO->FreeSpace),file_size($dO->TotalSize)]);
+	}else{
+	    array_push($result,[$dO->DriveLetter . ":",$n,$s]);
+	}
+	
     }
 	header('Content-Type: application/json');
 	echo json_encode($result);
@@ -34,7 +39,11 @@ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
 		$dataline = preg_replace("!\s+!",",",$line);
 		$dataline = explode(",",$dataline);
 		if ($dataline[0] != "tmpfs" && $dataline[0] != "Filesystem"){
-			array_push($result,[$dataline[5],$dataline[0],$dataline[3] . "/" . $dataline[1]]);
+		    	if (isset($_GET["nomerge"])){
+		    	    array_push($result,[$dataline[5],$dataline[0],$dataline[3] , $dataline[1]]);
+		    	}else{
+		    	    array_push($result,[$dataline[5],$dataline[0],$dataline[3] . "/" . $dataline[1]]);
+		    	}
 		}
 	}
 	header('Content-Type: application/json');

+ 8 - 2
src/SystemAOB/functions/system_statistic/searchFile.php

@@ -44,7 +44,7 @@ if(!isset($_GET["keyword"])){
 				<div class="sixteen wide field">
 					<label>Keyword</label>
 					<input type="text" placeholder="Keyword" id="keywordInput">
-					&nbsp;
+					 
 					<button onclick="updatelist()" id="btn" class="ts button">Search</button>
 				</div>
 				</div>
@@ -89,6 +89,8 @@ if(!isset($_GET["keyword"])){
 			var targetFilepath = "";
 			var targetFilename = "";
 			function updatelist(){
+			    $("body").parent().append($("#operationSelect")); //Move the selection box away first
+			    $("#operationSelect").hide(); //Hide the selection box
 				$('#mainmenu').html('<div class="item"><div class="ts active centered inline loader"></div></div>');
 				$.get("searchFile.php?keyword=" + $('#keywordInput').val(), function(data, status){
 					$('#mainmenu').html("");
@@ -322,10 +324,14 @@ if(!isset($_GET["keyword"])){
 	$files = getDirContents('../../../');
 	$result = [];
 	foreach ($files as $file){
-		if (strpos(basename($file),$keyword)){
+		if (strpos(basename($file),$keyword) !== false){
 			$relativePath = getRelativePath(realpath("../../../"),$file);
 			$decodedName = hexFilenameDecoder(basename($file));
 			array_push($result,[$relativePath,$decodedName]);
+		}else if (strpos(basename($file),bin2hex($keyword)) !== false){
+		    $relativePath = getRelativePath(realpath("../../../"),$file);
+			$decodedName = hexFilenameDecoder(basename($file));
+			array_push($result,[$relativePath,$decodedName]);
 		}
 	}
 	header('Content-Type: application/json');

BIN
src/SystemAOB/system/diskmg/DiskmgWin.exe


+ 13 - 0
src/SystemAOB/system/diskmg/definition.php

@@ -0,0 +1,13 @@
+<?php
+$supportedFormats = ["ntfs","vfat"];
+$allowedDirectories = ["/media", "/var/www"];
+if (file_exists("../../functions/personalization/sysconf/fsaccess.config")){
+    $allowedDirectories = [];
+    $settings = json_decode(file_get_contents("../../functions/personalization/sysconf/fsaccess.config"),true);
+    $paths = $settings["syspaths"][3];
+    $paths = explode(";",$paths);
+    foreach ($paths as $path){
+        array_push($allowedDirectories,$path);
+    }
+}
+?>

+ 18 - 0
src/SystemAOB/system/diskmg/diskmg.php

@@ -0,0 +1,18 @@
+<?php
+$partition = json_decode(shell_exec("lsblk -b --json"));
+$format = json_decode(shell_exec("lsblk -f -b --json"));
+$freeSpace =shell_exec("df");
+while(strpos($freeSpace,"  ") !== false){
+    $freeSpace = str_replace("  "," ",$freeSpace);
+}
+$freeSpace = explode("\n",$freeSpace);
+$freeSpaceParsed = [];
+foreach ($freeSpace as $part){
+    $part = explode(" ",$part);
+    array_push($freeSpaceParsed,$part);
+}
+//Throw away the table header
+array_shift($freeSpaceParsed);
+header('Content-Type: application/json');
+echo json_encode([$partition,$format,$freeSpaceParsed]);
+?>

+ 23 - 0
src/SystemAOB/system/diskmg/diskmgWin.php

@@ -0,0 +1,23 @@
+<?php
+//This php requires DiskmgWin.exe to work.
+//The three numbers following the disk info is Available space to current user, Total available space, Disk total space in bytes.
+include_once("../../../auth.php");
+if (isset($_GET['partition'])){
+    $output = shell_exec("DiskmgWin.exe -d");
+}else{
+   $output = shell_exec("DiskmgWin.exe"); 
+}
+$tmp = explode(";",trim($output));
+$emptyCheck = array_pop($tmp);
+if (trim($emptyCheck) !== ""){
+    //Check if the last item is empty. If it is not, push it back into the queue.
+    array_push($tmp,$emptyCheck);
+}
+$diskInfo = [];
+foreach ($tmp as $disk){
+    array_push($diskInfo,explode(",",$disk));
+}
+
+header('Content-Type: application/json');
+echo json_encode($diskInfo);
+?>

+ 54 - 0
src/SystemAOB/system/diskmg/formatTool.php

@@ -0,0 +1,54 @@
+<?php
+include_once("../auth.php");
+include_once("definition.php");
+
+if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+    die("ERROR. Window is currently not supported.");
+}else{
+    //Linux environment
+    if (isset($_GET['dev']) && isset($_GET['format'])){
+        $dev = $_GET['dev'];
+        $format = $_GET['format'];
+        //Check if dev id correct
+        preg_match('/sd[a-z][1-9]/', $dev, $result);
+        if (count($result) == 0){
+            die("ERROR. Invalid device ID. " . $dev . " given.");
+        }
+        
+        //Check if dev exists
+        if (!file_exists("/dev/" . $dev)){
+            die("ERROR. Device not exists.");
+        }
+        
+        //Check if format is supported
+        if (!in_array($format,$supportedFormats)){
+            die("ERROR. Not supported format.");
+        }
+        
+        //Check if the dev is mounted. Unmount it if nessary.
+        $out = shell_exec("lsblk -f -b --json | grep " . $dev);
+        if (strlen(trim($out)) == 0){
+            //Something strange happended
+            die("ERROR. Unknown error has occured. lsblk return no result.");
+        }
+        $out = json_decode(trim($out),true);
+        if ($out["mountpoint"] !== null){
+            //Unmount the dev if it is mounted
+            shell_exec("sudo umount " . $out["mountpoint"]);
+        }
+        
+        //Unmount once more on dev ID just for safty
+        shell_exec("sudo umount /dev/" . $dev);
+        
+        //Drive ready to be formatted.
+        if ($format == "ntfs"){
+            shell_exec("sudo mkfs.ntfs -f /dev/" . $dev);
+        }else if ($format == "vfat"){
+            shell_exec("sudo mkfs.vfat /dev/" . $dev);
+        }
+        echo "DONE";
+    }else{
+        die("ERROR. Called with invalid paramters.");
+    }
+}
+?>

+ 722 - 0
src/SystemAOB/system/diskmg/index.php

@@ -0,0 +1,722 @@
+<?php
+include_once("../../../auth.php");
+
+?>
+<html>
+    <head>
+        <meta charset="UTF-8">
+        <link rel="stylesheet" href="../../../script/tocas/tocas.css">
+        <script type='text/javascript' src="../../../script/tocas/tocas.js"></script>
+        <script src="../../../script/jquery.min.js"></script>
+        <script src="../../../script/ao_module.js"></script>
+        <title>Diskmg</title>
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <style>
+            .customFitted.item{
+                padding-top:5px !important;
+                padding-bottom:5px !important;
+            }
+            #diskListTable{
+                max-height:300px !important;
+            }
+            #diskVisualization{
+                overflow-y:auto;
+            }
+            .diskPartTable{
+                width:100%;
+                border-bottom:1px solid #9c9c9c;
+                overflow-x:hidden;
+            }
+            .sideblock{
+                background-color:#e8e8e8;
+                height:100px;
+                width:100px;
+                padding:8px;
+                border-right:1px solid #9c9c9c;
+                font-size:90%;
+                display:inline-block;
+            }
+            .partitionRepresentation{
+                border:4px solid #e8e8e8;
+                display:inline-block;
+                height:100px;
+                vertical-align: top;
+                overflow:hidden;
+                border-left:1px solid #6e6e6e;
+                cursor:pointer;
+            }
+            .partitionTopBar{
+                background-color:#224ce3;
+                width:100%;
+                height:15px;
+                margin-bottom:3px;
+            }
+            .partitionTopBar.unallocate{
+                 background-color:#1f1f1f;
+            }
+            .partitionTopBar.unmounted{
+                 background-color:#ab8a29;
+            }
+            .partitionDescription{
+                padding-left:8px;
+                padding:3px;
+            }
+            #rightClickMenu{
+                position:absolute;
+            }
+            .selectable:hover{
+                background-color:#f0f0f0;
+            }
+            .focusedPart{
+              background-image: linear-gradient(45deg, #e6e6e6 16.67%, #ffffff 16.67%, #ffffff 50%, #e6e6e6 50%, #e6e6e6 66.67%, #ffffff 66.67%, #ffffff 100%);
+background-size: 12.73px 12.73px;
+            }
+            .disabled{
+                background-color:#e6e6e6;
+                color:#787878 !important;
+                cursor:no-drop !important;
+            }
+            .funcmenu{
+                position:fixed;
+                top:10%;
+                right:20%;
+                left:20%;
+                bottom:10%;
+                overflow-y:auto;
+                z-index:100;
+                background-color:#f7f7f7;
+                padding:12px;
+                display:none;
+                border: 1px solid #9c9c9c;
+            }
+            .functMenuDimmer{
+                z-index:90;
+                position:absolute;
+                width:100%;
+                height:100%;
+                left:0px;
+                top:0px;
+                background:rgba(48,48,48,0.5);
+                display:none;
+            }
+            .funcmenuBottom{
+                position:absolute;
+                width:100%;
+                bottom:0px;
+                left:0px;
+                padding:12px;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="diskListTable">
+            <table class="ts celled striped attached table" style="padding-left:3px;padding-right:3px;">
+                <thead>
+                    <tr>
+                        <th>
+                            Volume
+                        </th>
+                        <?php
+                        $mode = "linux";
+                        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+                            echo '  <th>
+                                        Volume_Label
+                                    </th>
+                                    <th>
+                                        Type
+                                    </th>';
+                                    $mode = "window";
+                        } else {
+                            echo '  <th>
+                                        MountPt
+                                    </th>';
+                        }
+                                        
+                        ?>
+                       
+                        <th>
+                            File_System
+                        </th>
+                        <th>
+                            Capacity
+                        </th>
+                        <th>
+                            Free_Space
+                        </th>
+                        <th>
+                            %_Free
+                        </th>
+                    </tr>
+                </thead>
+                <tbody id="diskInfoTable">
+                    <tr>
+                        <td class="collapsing">
+                            <i class="disk outline icon"></i>/dev/sda1
+                        </td>
+                        <td class="collapsing">/media/storage1</td>
+                        <td class="right aligned collapsing">NTFS</td>
+                        <td class="right aligned collapsing">64 GB</td>
+                        <td class="right aligned collapsing">12.5 GB</td>
+                        <td class="right aligned collapsing">19.7%</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <div id="diskVisualization">
+            <div class="diskPartTable">
+                <div class="sideblock">
+                    <i class="disk outline icon" style="margin-right:0px;font-weight: bold;"></i>
+                    <b style="font-weight: bold;">Drive 0</b><br>
+                    N/A
+                </div><div class="partitionRepresentation" style="width:calc(100% - 150px);">
+                    <div class="partitionTopBar"></div>
+                    <div class="partitionDescription">
+                       Connecting to Virtual Disk Services
+                    </div>
+                </div>
+            </div>
+        </div>
+    <div id="rightClickMenu" class="ts contextmenu">
+        <div id="openbtn" class="item disabled selectable" onClick="openInFileExplorer(this);">
+            <i class="folder open icon"></i> Open
+        </div>
+        <div id="formatDisk" class="item selectable" onClick="toggleFormatInterface(this);">
+            <i class="disk outline icon"></i> Format Disk
+        </div>
+        <div id="mtbtn" class="item selectable" onClick="toggleMount(this);">
+            <i class="usb icon"></i> Mount
+        </div>
+    </div>
+    <!-- Sections for functional menus-->
+    <div id="formatOptions" class="funcmenu">
+        <div class="ts header">
+            Disk Format
+            <div class="sub header" style="font-weight:120%;color:red;">Warning! The format process will wipe all data from the selected partition.</div>
+        </div>
+        <div class="ts inverted negative segment">
+            <p style="font-size:120%;">Danger Zone</p>
+            <p>Format on any drive or partition will wipe all data within that drive or partition. Please make sure you have backup all necessary files and your drive / partition selection is correct.</p>
+        </div>
+        <div class="ts header">
+            <i class="disk outline icon"></i>
+            <div id="selectedDiskDisplay" class="content">
+                /dev/sda1 (120 GB)
+            </div>
+        </div>
+        <div class="ts form">
+            <div class="field">
+                <label>Target File System Format</label>
+                <div class="ts checkboxes">
+                    <div class="ts radio checkbox">
+                        <input id="ntfs" type="radio" name="format" checked>
+                        <label for="ntfs">NTFS</label>
+                    </div>
+                    <div class="ts radio checkbox">
+                        <input id="vfat" type="radio" name="format">
+                        <label for="vfat">VFAT</label>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="funcmenuBottom" align="right">
+            <button class="ts tiny negative button" onClick="formatThisDev();">Format</button>
+            <button class="ts tiny button" onClick="hideAllFuncMenu();">Close</button>
+        </div>
+    </div>
+    <div id="mountOptions" class="funcmenu">
+        <div class="ts header">
+            Disk Mount
+            <div class="sub header">Select a mount point for this device</div>
+        </div>
+        <div class="ts segmented list" style="max-height:300px;overflow-y:auto;">
+            <?php
+            if ($mode == "window"){
+                
+            }else{
+                $mntpt = glob("/media/*");
+                foreach ($mntpt as $pt){
+                    echo '<div class="mountpt selectable item" style="cursor:pointer;" ondblclick="mountThisDev(this)">' . $pt . '</div>';
+                }
+            }
+            ?>
+            <div class="mountpt item userdefine">
+                <p>User defined mount point</p>
+                <div class="ts fluid mini input">
+                    <input id="userDefinedMountPoint" type="text" placeholder="/">
+                </div>
+            </div>
+        </div>
+        
+        <div class="funcmenuBottom" align="right">
+            <button class="ts tiny button" onClick="mountThisDev();">Mount</button>
+            <button class="ts tiny button" onClick="hideAllFuncMenu();">Close</button>
+        </div>
+    </div>
+    
+    <!-- dimmers-->
+     <div id="loaderUI" class="ts active inverted  dimmer" style="display:none;">
+        <div class="ts text loader">Waiting for System Response</div>
+    </div>
+    <div class="functMenuDimmer" onClick="hideAllFuncMenu();">
+        
+    </div>
+    <div style="display:none;">
+        <div id="data_searchMode"><?php echo $mode; ?></div>
+    </div>
+    <script>
+        var mode = $("#data_searchMode").text().trim();
+        var viewMode = "human"; //Accept {human / raw}
+        var diskInformation; //Tmp variable for holding disk return results
+        var displayScaleRatio = 0.2; //Maxium differential ratio, default 0.3, it means the minium disk will show as 70% screen width
+        var fwmode = false;
+        var formatPendingDevInfo;
+        
+        //Init floatWindow events
+        if (ao_module_virtualDesktop && !parent.underNaviEnv){
+            ao_module_setWindowIcon("disk outline");
+            ao_module_setWindowTitle("Disk Manager");
+            fwmode = true;
+        }else{
+            
+        }
+        
+        //Init Window only events
+        if (mode == "window"){
+            $("#formatOptions").remove();
+            $("#mountOptions").remove();
+        }
+        
+        //Init data loading process
+        initView();
+        initPartitionTable();
+        
+        //Mount pt selection interface
+        $(".mountpt").on('click',function(e){
+            $(".selected").removeClass("selected");
+            $(this).addClass("selected");
+        });
+        
+        function hideAllFuncMenu(){
+            $(".funcmenu").fadeOut('fast');
+            $(".functMenuDimmer").fadeOut('fast');
+        }
+        
+        function formatThisDev(){
+            var targetFormat = $("input[name='format']:checked").attr("id");
+            var targetDisk = formatPendingDevInfo;
+            if(targetFormat){
+                $("#loaderUI").show();
+                if (confirm("THIS OPERATION WILL WIPE ALL DATA ON /dev/" + targetDisk[0] + ". ARE YOU SURE?")){
+                    $("#formatOptions").fadeOut('fast');
+                    $(".functMenuDimmer").fadeOut('fast');
+                    $.get("formatTool.php?dev=" + targetDisk[0] + "&format=" + targetFormat,function(e){
+                        if (e.includes("ERROR")){
+                            alert(e);
+                        }
+                        initView();
+                        initPartitionTable();
+                        $("#loaderUI").hide();
+                    });
+                }else{
+                    $("#loaderUI").hide();
+                }
+            }
+        }
+        
+        function toggleFormatInterface(btnObject){
+            if ($(btnObject).hasClass("disabled") == true){
+                return;
+            }
+            $("#formatOptions").fadeIn('fast');
+            $(".functMenuDimmer").fadeIn('fast');
+            hideRightclickMenu();
+            var diskInfo = $(".focusedPart").attr("metadata");
+            diskInfo = ao_module_utils.attrToObject(diskInfo);
+            formatPendingDevInfo = diskInfo;
+            $("#selectedDiskDisplay").text(diskInfo[0] + " (" + bytesToSize(parseInt(diskInfo[5])) + ") ");
+        }
+        
+        function mountThisDev(object=null){
+            if (object !== null && !$(object).hasClass(".selected.item")){
+                $(".selected").removeClass("selected");
+                $(object).addClass("selected");
+            }
+            var selectedMpt = $(".selected.item");
+            var mountPoint = $(selectedMpt).text().trim();
+            if (selectedMpt.hasClass("userdefine")){
+                var mountPoint = $("#userDefinedMountPoint").val();
+            }
+             $("#loaderUI").show();
+             var diskInfo = $(".focusedPart").attr("metadata");
+             diskInfo = ao_module_utils.attrToObject(diskInfo);
+             $.get("mountTool.php?dev=" + diskInfo[0] + "&format=" + diskInfo[2] + "&mnt=" + mountPoint,function(data){
+                if (data.includes("ERROR")){
+                    alert(data);
+                    $("#loaderUI").hide();
+                    return;
+                }
+                //Reload the UI
+                initView();
+                initPartitionTable();
+                $("#loaderUI").hide();
+                $("#mountOptions").fadeOut('fast');
+                $(".functMenuDimmer").fadeOut('fast');
+            });
+        }
+        
+        function toggleMount(btnObject){
+            if ($(btnObject).hasClass("disabled") == true){
+                return;
+            }
+            var diskInfo = $(".focusedPart").attr("metadata");
+            diskInfo = ao_module_utils.attrToObject(diskInfo);
+            if (diskInfo[3] == false){
+                //Mount disk
+                $("#mountOptions").fadeIn('fast');
+                $(".functMenuDimmer").fadeIn('fast');
+                
+            }else{
+                //Unmount disk
+                var dev = diskInfo[0];
+                var mnt = diskInfo[1];
+                var format = diskInfo[2];
+                hideRightclickMenu();
+                $("#loaderUI").show();
+                $.get("mountTool.php?dev=" + dev + "&format=" + format + "&mnt=" + mnt + "&unmount",function(data){
+                    console.log(data);
+                    //Reload the UI
+                     initView();
+                     initPartitionTable();
+                     $("#loaderUI").hide();
+                });
+            }
+            hideRightclickMenu();
+        }
+        
+        function hideRightclickMenu(){
+            $("#rightClickMenu").hide();
+        }
+        function openInFileExplorer(btnObject){
+            if ($(btnObject).hasClass('disabled')){
+                return;
+            }
+            var diskInfo = $(".focusedPart").attr("metadata");
+            diskInfo = ao_module_utils.attrToObject(diskInfo);
+            if (diskInfo[3] == true){
+                //This disk is mounted
+                var uid = Date.now();
+                if (fwmode){
+                    ao_module_newfw("SystemAOB/functions/file_system/index.php?controlLv=2&dir=" + diskInfo[1],"Loading", "folder open outline",uid,1080,580,undefined,undefined,true,true);
+                }else if (parent.underNaviEnv){
+                    var uid = Date.now();
+		            parent.parent.newEmbededWindow("SystemAOB/functions/file_system/index.php?controlLv=2&dir=" + diskInfo[1], "Loading", "folder open outline",uid,1080,580,undefined,undefined,true,true);
+                }else{
+                    window.open("../../functions/file_system/index.php?controlLv=2&dir=" + diskInfo[1]);
+                }
+            }
+            hideRightclickMenu();
+        }
+        
+        function createEventHooks(){
+            $(".partitionRepresentation").contextmenu(function(e){
+                if (mode == "window"){
+                    //Switch back to normal menu when under window mode
+                    return true;
+                }
+                var px = e.pageX;
+                var py = e.pageY;
+                $("#rightClickMenu").css({"left": px + "px", "top": py + "px"});
+                $("#rightClickMenu").show();
+                console.log(e.target);
+                $(".focusedPart").removeClass("focusedPart");
+                var partbody =  $(e.target);
+                if ($(e.target).parent().hasClass("partitionRepresentation")){
+                    //Clicked on the child instead.
+                    $(e.target).parent().addClass("focusedPart");
+                    partbody =  $(e.target).parent();
+                }else{
+                    //Click on the representation body.
+                    $(e.target).addClass("focusedPart");
+                }
+                
+                //Create a custom context menu for the operation
+                var partInfo = ao_module_utils.attrToObject(partbody.attr("metadata"));
+                console.log(partInfo);
+                if (partInfo[3] == true){
+                    //This disk is mounted. Provide unmount btn
+                    if (partInfo[1] == "/" || partInfo[1] == "/boot"){
+                        //No, you can't unmount root nor format it
+                        $("#mtbtn").addClass("disabled");
+                        $("#formatDisk").addClass("disabled");
+                    }else{
+                        $("#mtbtn").removeClass("disabled");
+                        $("#formatDisk").removeClass("disabled");
+                    }
+                    $("#mtbtn").html('<i class="usb icon"></i> Safely Remove Hardware');
+                    if (partInfo[1].substring(0,6) == "/media"){
+                         //This can be opened
+                         $("#openbtn").removeClass("disabled");
+                    }else{
+                         $("#openbtn").addClass("disabled");
+                    }
+                }else{
+                    //This disk is not mounted. Provide mount btn
+                    $("#mtbtn").html('<i class="usb icon"></i> Mount Drive');
+                    $("#openbtn").addClass("disabled");
+                    $("#mtbtn").removeClass("disabled");
+                    $("#formatDisk").removeClass("disabled");
+                }
+                //Prevent browser menu from showing
+                return false;
+            });
+        }
+        
+        function adjustPartitionViewHeight(){
+            $("#diskVisualization").css("height",window.innerHeight - $("#diskListTable").height() + "px");
+        }
+        
+        function initView(){
+            if (mode == "window"){
+                //Runing on top of Window Host
+                $.get("diskmgWin.php",function(data){
+                    $("#diskInfoTable").html("");
+                    if (data.includes("ERROR") == false){
+                       for (var i = 0; i < data.length; i++){
+                           var thisDisk = data[i];
+                           $("#diskInfoTable").append('<tr>\
+                            <td class="collapsing">\
+                                <i class="disk outline icon"></i>' + thisDisk[0] + '\
+                            </td>\
+                            <td class="">' + thisDisk[2] + '</td>\
+                            <td class="collapsing">' + thisDisk[1] + '</td>\
+                            <td class="right aligned collapsing">' + thisDisk[3] + '</td>\
+                            <td class="right aligned collapsing">' + bytesToSize(thisDisk[6]) + '</td>\
+                            <td class="right aligned collapsing">' + bytesToSize(thisDisk[5]) + '</td>\
+                            <td class="right aligned collapsing">' + Math.round(thisDisk[5] / thisDisk[6] * 100) + '%</td>\
+                        </tr>');
+                       }
+                    }
+                });
+            }else{
+                //Runing on top of Linux Host
+                $.get("diskmg.php",function(data){
+                    $("#diskInfoTable").html("");
+                    if (data.includes("ERROR") == false){
+                        var disks = data[0]["blockdevices"];
+                        var partitions = data[1];
+                       for (var i = 0; i < disks.length; i++){
+                           var thisDisk = disks[i]["children"];
+                           if (thisDisk === undefined){
+                               break;
+                           }
+                           for (var j =0; j < thisDisk.length; j++){
+                               var thisPartition = thisDisk[j];
+                               var mtPoint = thisPartition["mountpoint"];
+                               if (mtPoint === null){
+                                   mtPoint = "Not Mounted";
+                               }
+                               //Get the filesystem from another command return results
+                               var disksFormats = data[1]["blockdevices"][i]["children"][j];
+                               var fstype = disksFormats["fstype"];
+                               if (fstype === null){
+                                   fstype = "raw";
+                               }
+                               //console.log(disksFormats);
+                               
+                               //Read freesapce from the last command return results
+                               var freeSpacesRatio = "0%";
+                               for (var k =0; k < data[2].length; k++){
+                                   if (data[2][k][5] == thisPartition["mountpoint"]){
+                                       //This device is mounted at the same path as current partition. It should be this volume
+                                       freeSpacesRatio = data[2][k][4];
+                                   }
+                               }
+                               if (freeSpacesRatio === undefined){
+                                   freeSpacesRatio = "0%";
+                               }
+                               var numericalFreeSpace = parseInt(freeSpacesRatio.replace("%","")) * thisPartition["size"] / 100;
+                               
+                               //Print the results to the interface
+                               //console.log(thisPartition);
+                               $("#diskInfoTable").append('<tr>\
+                                    <td class="collapsing">\
+                                        <i class="disk outline icon"></i>' + thisPartition["name"] + '\
+                                    </td>\
+                                    <td class="">' +  mtPoint + '</td>\
+                                    <td class="right aligned collapsing">' + fstype + '</td>\
+                                    <td class="right aligned collapsing">' + bytesToSize(thisPartition["size"]) + '</td>\
+                                    <td class="right aligned collapsing">' + bytesToSize(numericalFreeSpace) + '</td>\
+                                    <td class="right aligned collapsing">' + freeSpacesRatio + '</td>\
+                                </tr>');
+                           }
+                           
+                       }
+                    }
+                });
+            }
+        }
+    
+        
+        function initPartitionTable(){
+             if (mode == "window"){
+                  $.get("diskmgWin.php?partition",function(data){
+                    var disks = {};
+                    for(var i =0; i < data.length; i++){
+                        var thisPart = data[i];
+                        //var diskID = thisPart[9].replace(":","");
+                        var diskID = thisPart[0].replace(/\\+.+\\/,"");
+                        if (disks == undefined || disks[diskID] == undefined){
+                            disks[diskID] = {"partitionsTotalSize":thisPart[14],"partitionNames":[thisPart[16]],"partitionID":[ thisPart[9]],"partitionVolume":[thisPart[14]],"Type":[thisPart[5]],"Mounted":[thisPart[4]=="True"],"Format":[thisPart[12]]};
+                        }else{
+                           disks[diskID]["partitionsTotalSize"] = parseInt(disks[diskID]["partitionsTotalSize"]) + parseInt(thisPart[14]);
+                           disks[diskID]["partitionVolume"].push(thisPart[14]);
+                           disks[diskID]["partitionNames"].push(thisPart[16]);
+                           disks[diskID]["partitionID"].push(thisPart[9]);
+                           disks[diskID]["Type"].push(thisPart[5]);
+                           disks[diskID]["Format"].push(thisPart[12]);
+                           disks[diskID]["Mounted"].push(thisPart[4]=="True");
+                        }
+                    }
+                    diskInformation = JSON.parse(JSON.stringify(disks));
+                    drawDartitionTable();
+                });
+                
+             }else{
+                 //This is a Linux Host
+                  $.get("diskmg.php",function(data){
+                      var disks = {};
+                      var diskInfo = data[0]["blockdevices"];
+                      for (var i =0; i < diskInfo.length; i++){
+                          var thisDisk = diskInfo[i];
+                          var diskID = thisDisk["name"];
+                          if (thisDisk["children"] === undefined){
+                              //This disk do not have any child. Assume a large read-only raw partition.
+                              disks[diskID] = {"partitionsTotalSize":0,"partitionNames":["Hotplug"],"partitionID":["✖"],"partitionVolume":[0],"Type":[thisPart["type"]],"Mounted":[thisPart["mountpoint"] !== null],"Format":[""]};
+                              break;
+                          }
+                          for (var j =0; j < thisDisk["children"].length;j++){
+                            var thisPart = thisDisk["children"][j];
+                            var disksFormats = data[1]["blockdevices"][i]["children"][j];
+                            console.log(disksFormats);
+                            if (disks == undefined || disks[diskID] == undefined){
+                                disks[diskID] = {"partitionsTotalSize":thisPart["size"],"partitionNames":[thisPart["mountpoint"]],"partitionID":[thisPart["name"]],"partitionVolume":[thisPart["size"]],"Type":[thisPart["type"]],"Mounted":[thisPart["mountpoint"] !== null],"Format":[disksFormats["fstype"]]};
+                            }else{
+                               disks[diskID]["partitionsTotalSize"] = parseInt(disks[diskID]["partitionsTotalSize"]) + parseInt(thisPart["size"]);
+                               disks[diskID]["partitionVolume"].push(thisPart["size"]);
+                               disks[diskID]["partitionNames"].push(thisPart["mountpoint"]);
+                               disks[diskID]["partitionID"].push(thisPart["name"]);
+                               disks[diskID]["Type"].push(thisPart["type"]);
+                               disks[diskID]["Format"].push(disksFormats["fstype"]);
+                               disks[diskID]["Mounted"].push(thisPart["mountpoint"] !== null);
+                            }
+                          }
+                      }
+                      diskInformation = JSON.parse(JSON.stringify(disks));
+                      drawDartitionTable();
+                  });
+                 
+             }
+             
+        }
+        
+        function drawDartitionTable(){
+            var disks = JSON.parse(JSON.stringify(diskInformation));
+            //Clear the old diskpart table
+            $("#diskVisualization").html("");
+            //Render the partition table
+            var maxWidth = window.innerWidth * 0.95 - 110;
+            var maxCapDisk = -1;
+            var keys = [];
+            for (key in disks){
+                keys.push(key);
+                var thisDiskSize = disks[key]["partitionsTotalSize"];
+                if (thisDiskSize > maxCapDisk){
+                    maxCapDisk = thisDiskSize;
+                }
+            }
+            
+            keys.sort();
+            for (var i =0; i < keys.length; i++){
+                var diskInfo = disks[keys[i]];
+                var diskID = keys[i];
+                var mountState = "Mounted";
+                var shortenType = diskInfo["Type"][0].split(" ").shift();
+                var thisMaxWidth = maxWidth - (1- (diskInfo["partitionsTotalSize"] / maxCapDisk)) * (window.innerWidth * displayScaleRatio);
+                if (diskInfo["Mounted"] == false){
+                    mountState = "Unmounted";
+                }
+                //console.log(diskID,diskInfo);
+                //Append the disk info block
+                $("#diskVisualization").append('<div class="diskPartTable">');
+                $("#diskVisualization").append('<div class="sideblock">\
+                    <i class="disk outline icon" style="margin-right:0px;font-weight: bold;"></i>\
+                    <b style="font-weight: bold;">Drive ' + i + '</b><br>\
+                    ' + shortenType + '<br>\
+                    ' + bytesToSize(diskInfo["partitionsTotalSize"]) + '<br>\
+                    ' + mountState + '\
+                </div>');
+                var partitionIDs = diskInfo["partitionID"];
+                for (var k =0; k < partitionIDs.length; k++){
+                    var thisWidth = thisMaxWidth * (parseInt(diskInfo["partitionVolume"][k]) / diskInfo["partitionsTotalSize"]);
+                    var topbarExtraClass = "";
+                    if (diskInfo["partitionVolume"][k] == 0){
+                        topbarExtraClass = " unallocate";
+                    }else if (diskInfo["partitionNames"][k] === null){
+                        topbarExtraClass = " unmounted";
+                        diskInfo["partitionNames"][k] = "Not Mounted";
+                    }
+                    $("#diskVisualization").append('<div class="partitionRepresentation" style="width:' + thisWidth + 'px;" metaData="\
+                    ' + ao_module_utils.objectToAttr([diskInfo["partitionID"][k],diskInfo["partitionNames"][k],diskInfo["Format"][k],diskInfo["Mounted"][k],diskInfo["Type"][k],diskInfo["partitionVolume"][k]]) + '">\
+                        <div class="partitionTopBar' + topbarExtraClass + '"></div>\
+                        <div class="partitionDescription">\
+                            ' + diskInfo["partitionNames"][k] +" (" + diskInfo["partitionID"][k] + ')<br>\
+                            ' + bytesToSize(parseInt(diskInfo["partitionVolume"][k])) + ' ' + diskInfo["Format"][k] + '<br>\
+                        </div>\
+                    </div>');
+                }
+                $("#diskVisualization").append('</div>');
+            }
+            
+            setTimeout(function(){
+                adjustPartitionViewHeight();
+            },500);
+            createEventHooks();
+        }
+        
+        $(window).on("resize",function(){
+            adjustPartitionViewHeight();
+            drawDartitionTable();
+        });
+        
+        $("#diskVisualization").on('click',function(e){
+           var target = e.target;
+           //console.log($(target).parents(".partitionRepresentation"));
+           if ($(target).parents(".partitionRepresentation").length == 0 && !$(target).hasClass("partitionRepresentation")){
+               $("#rightClickMenu").hide();
+           }else if (e.button == 0){
+               if ($(target).parents(".partitionRepresentation").length > 0 || $(target).hasClass("partitionRepresentation")){
+                   $(".focusedPart").removeClass("focusedPart");
+                   if ($(target).parent().hasClass("partitionRepresentation")){
+                       $(target).parent().addClass("focusedPart");
+                   }else{
+                       $(target).addClass("focusedPart");
+                   }
+               }
+               $("#rightClickMenu").hide();
+           }
+        });
+        
+        function bytesToSize(bytes) {
+            if (viewMode == "human"){
+                var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+                 if (bytes == 0) return '0 Byte';
+                 var i = parseFloat(Math.floor(Math.log(bytes) / Math.log(1024)));
+                 return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
+            }else if (viewMode == "raw"){
+                return bytes + " B";
+            }
+           
+        }
+    </script>
+    </body>
+</html>

+ 72 - 0
src/SystemAOB/system/diskmg/mountTool.php

@@ -0,0 +1,72 @@
+<?php
+include_once("../../../auth.php");
+include_once("definition.php");
+if (isset($_GET['dev']) && isset($_GET['format']) && isset($_GET['mnt'])){
+    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+        die("ERROR. Call to system function not supported by your operating system.");
+    } else {
+        $targetDev = $_GET['dev'];
+        $format = $_GET['format'];
+        $mountPt = $_GET['mnt'];
+        //check id device id follows the sd* naming format
+        preg_match('/sd[a-z][1-9]/', $targetDev, $result);
+        if (count($result) > 0){
+            //Check if the device exists.
+            if (!file_exists("/dev/" . $targetDev)){
+                die("ERROR. Target device ID not found.");
+            }
+        }else{
+            die("ERROR. Invalid device ID");
+        }
+        
+        //Check if the given format is supported by this tool
+        if (!in_array($format,$supportedFormats)){
+            die("ERROR. Format not supported.");
+        }
+        
+        //Check if the mount point exists.
+        if (file_exists($mountPt) && checkWithinAllowedDirectories($mountPt)){
+            
+        }else{
+            die("ERROR. Invalid Mounting Point location or mount point not exists.");
+        }
+        
+       
+       if (isset($_GET['unmount'])){
+            //Start mounting
+            $ouput = shell_exec("sudo umount " . $mountPt);
+            echo $output;
+            exit(0);
+       }else{
+            //Start mounting
+        if ($format == "ntfs"){
+             $ouput = shell_exec("sudo mount -t ntfs-3g /dev/" . $targetDev . " " . $mountPt);
+        }else if ($format == "vfat"){
+             $ouput = shell_exec("sudo mount -t vfat /dev/" . $targetDev . " " . $mountPt);
+        }else{
+            die("ERROR. Unknown error occured.");
+        }
+        echo $output;
+        exit(0);
+       }
+       
+    }
+    
+}else{
+    die("ERROR. Call with invalid paramters.");
+}
+
+function checkWithinAllowedDirectories($dir){
+    global $allowedDirectories;
+    $dir = realpath($dir);
+    foreach ($allowedDirectories as $validDir){
+        $validDir = realpath($validDir);
+        if (strpos($dir,$validDir) === 0){
+            //matched at least one valid dir
+            return true;
+        }
+    }
+    return false;
+    
+}
+?>

+ 431 - 0
src/SystemAOB/utilities/paint.php

@@ -0,0 +1,431 @@
+<?php
+include_once("../../auth.php");
+if (isset($_POST['saveImage']) && isset($_POST['savePath']) && isset($_POST['filename'])){
+     $encodedData = $_POST['saveImage'];
+     $saveFilepath = "../../" . $_POST['savePath'];
+     $filename = $_POST['filename'];
+     $img = str_replace('data:image/png;base64,', '', $encodedData);
+     $img = str_replace(' ', '+', $img);
+     $fileData = base64_decode($img);
+     if (strpos(realpath($saveFilepath),realpath($rootPath)) === 0 || strpos(realpath($saveFilepath),"/media") === 0){
+         //This file is located in localStorage or media
+         file_put_contents($saveFilepath .$filename, $fileData);
+         echo "DONE";
+     }else{
+         die("ERROR. Filepath not in AOR nor external storage path.");
+     }
+     exit(0);
+}
+?>
+<html>
+    <head>
+        <title>ArOZ Paint</title>
+        <link rel="stylesheet" href="../../script/tocas/tocas.css">
+        <script src="../../script/jquery.min.js"></script>
+        <script src="../../script/ao_module.js"></script>
+        <style>
+            * {
+              box-sizing: border-box;
+            }
+            
+            main {
+              width: 800px;
+              border: 1px solid #e0e0e0;
+              margin: 0 auto;
+              display: flex;
+              flex-grow: 1;
+            }
+            
+            .left-block {
+              width: 160px;
+              border-right: 1px solid #e0e0e0;
+            }
+            
+            .colors {
+              background-color: #ece8e8;
+              text-align: center;
+              padding-bottom: 5px;
+              padding-top: 10px;
+            }
+            
+            .colors button {
+              display: inline-block;
+              border: 1px solid #00000026;
+              border-radius: 0;
+              outline: none;
+              cursor: pointer;
+              width: 20px;
+              height: 20px;
+              margin-bottom: 5px
+            }
+            
+            .colors button:nth-of-type(1) {
+              background-color: #0000ff;
+            }
+            
+            .colors button:nth-of-type(2) {
+              background-color: #009fff;
+            }
+            
+            .colors button:nth-of-type(3) {
+              background-color: #0fffff;
+            }
+            
+            .colors button:nth-of-type(4) {
+              background-color: #bfffff;
+            }
+            
+            .colors button:nth-of-type(5) {
+              background-color: #000000;
+            }
+            
+            .colors button:nth-of-type(6) {
+              background-color: #333333;
+            }
+            
+            .colors button:nth-of-type(7) {
+              background-color: #666666;
+            }
+            
+            .colors button:nth-of-type(8) {
+              background-color: #999999;
+            }
+            
+            .colors button:nth-of-type(9) {
+              background-color: #ffcc66;
+            }
+            
+            .colors button:nth-of-type(10) {
+              background-color: #ffcc00;
+            }
+            
+            .colors button:nth-of-type(11) {
+              background-color: #ffff00;
+            }
+            
+            .colors button:nth-of-type(12) {
+              background-color: #ffff99;
+            }
+            
+            .colors button:nth-of-type(13) {
+              background-color: #003300;
+            }
+            
+            .colors button:nth-of-type(14) {
+              background-color: #555000;
+            }
+            
+            .colors button:nth-of-type(15) {
+              background-color: #00ff00;
+            }
+            
+            .colors button:nth-of-type(16) {
+              background-color: #99ff99;
+            }
+            
+            .colors button:nth-of-type(17) {
+              background-color: #f00000;
+            }
+            
+            .colors button:nth-of-type(18) {
+              background-color: #ff6600;
+            }
+            
+            .colors button:nth-of-type(19) {
+              background-color: #ff9933;
+            }
+            
+            .colors button:nth-of-type(20) {
+              background-color: #f5deb3;
+            }
+            
+            .colors button:nth-of-type(21) {
+              background-color: #330000;
+            }
+            
+            .colors button:nth-of-type(22) {
+              background-color: #663300;
+            }
+            
+            .colors button:nth-of-type(23) {
+              background-color: #cc6600;
+            }
+            
+            .colors button:nth-of-type(24) {
+              background-color: #deb887;
+            }
+            
+            .colors button:nth-of-type(25) {
+              background-color: #aa0fff;
+            }
+            
+            .colors button:nth-of-type(26) {
+              background-color: #cc66cc;
+            }
+            
+            .colors button:nth-of-type(27) {
+              background-color: #ff66ff;
+            }
+            
+            .colors button:nth-of-type(28) {
+              background-color: #ff99ff;
+            }
+            
+            .colors button:nth-of-type(29) {
+              background-color: #e8c4e8;
+            }
+            
+            .colors button:nth-of-type(30) {
+              background-color: #ffffff;
+            }
+            
+            .brushes {
+              //background-color: purple;
+              padding-top: 5px
+            }
+            
+            .brushes button {
+              display: block;
+              width: 100%;
+              border: 0;
+              border-radius: 0;
+              background-color: #ece8e8;
+              margin-bottom: 5px;
+              padding: 5px;
+              height: 30px;
+              outline: none;
+              position: relative;
+              cursor: pointer;
+            }
+            
+            .brushes button:after {
+              height: 1px;
+              display: block;
+              background: #808080;
+              content: '';
+            }
+            
+            .brushes button:nth-of-type(1):after {
+              height: 1px;
+            }
+            
+            .brushes button:nth-of-type(2):after {
+              height: 2px;
+            }
+            
+            .brushes button:nth-of-type(3):after {
+              height: 3px;
+            }
+            
+            .brushes button:nth-of-type(4):after {
+              height: 4px;
+            }
+            
+            .brushes button:nth-of-type(5):after {
+              height: 5px;
+            }
+            
+            .buttons {
+              height: 80px;
+              padding-top: 10px;
+            }
+            
+            .buttons button {
+              display: block;
+              width: 100%;
+              border: 0;
+              border-radius: 0;
+              background-color: #ece8e8;
+              margin-bottom: 5px;
+              padding: 5px;
+              height: 30px;
+              outline: none;
+              position: relative;
+              cursor: pointer;
+              font-size: 16px;
+            }
+            
+            .right-block {
+              width: 640px;
+            }
+            
+            #paint-canvas {
+              cursor:crosshair;
+            }
+
+        </style>
+    </head>
+    <body>
+            <main>
+              <div class="left-block">
+                <div class="colors">
+                  <button type="button" value="#0000ff"></button>
+                  <button type="button" value="#009fff"></button>
+                  <button type="button" value="#0fffff"></button>
+                  <button type="button" value="#bfffff"></button>
+                  <button type="button" value="#000000"></button>
+                  <button type="button" value="#333333"></button>
+                  <button type="button" value="#666666"></button>
+                  <button type="button" value="#999999"></button>
+                  <button type="button" value="#ffcc66"></button>
+                  <button type="button" value="#ffcc00"></button>
+                  <button type="button" value="#ffff00"></button>
+                  <button type="button" value="#ffff99"></button>
+                  <button type="button" value="#003300"></button>
+                  <button type="button" value="#555000"></button>
+                  <button type="button" value="#00ff00"></button>
+                  <button type="button" value="#99ff99"></button>
+                  <button type="button" value="#f00000"></button>
+                  <button type="button" value="#ff6600"></button>
+                  <button type="button" value="#ff9933"></button>
+                  <button type="button" value="#f5deb3"></button>
+                  <button type="button" value="#330000"></button>
+                  <button type="button" value="#663300"></button>
+                  <button type="button" value="#cc6600"></button>
+                  <button type="button" value="#deb887"></button>
+                  <button type="button" value="#aa0fff"></button>
+                  <button type="button" value="#cc66cc"></button>
+                  <button type="button" value="#ff66ff"></button>
+                  <button type="button" value="#ff99ff"></button>
+                  <button type="button" value="#e8c4e8"></button>
+                  <button type="button" value="#ffffff"></button>
+                </div>
+                <div class="brushes">
+                  <button type="button" value="1"></button>
+                  <button type="button" value="2"></button>
+                  <button type="button" value="3"></button>
+                  <button type="button" value="4"></button>
+                  <button type="button" value="5"></button>
+                </div>
+                <div class="buttons">
+                  <button id="clear" type="button">Clear</button>
+                  <button id="save" type="button">Save</button>
+                </div>
+              </div>
+              <div class="right-block">
+                <canvas id="paint-canvas" width="640" height="400"></canvas>
+              </div>
+            </main>
+            <script>
+            ao_module_setWindowSize(802,422,true);
+            ao_module_setFixedWindowSize();
+            ao_module_setWindowTitle("AO-Paint");
+            ao_module_setWindowIcon("paint brush");
+            var canvas = document.getElementById("paint-canvas");
+            var context = canvas.getContext("2d");
+            var boundings = canvas.getBoundingClientRect();
+                window.onload = function () {
+                  // Definitions
+                 
+                
+                  // Specifications
+                  var mouseX = 0;
+                  var mouseY = 0;
+                  context.strokeStyle = 'black'; // initial brush color
+                  context.lineWidth = 1; // initial brush width
+                  var isDrawing = false;
+                
+                
+                  // Handle Colors
+                  var colors = document.getElementsByClassName('colors')[0];
+                
+                  colors.addEventListener('click', function(event) {
+                    context.strokeStyle = event.target.value || 'black';
+                  });
+                
+                  // Handle Brushes
+                  var brushes = document.getElementsByClassName('brushes')[0];
+                
+                  brushes.addEventListener('click', function(event) {
+                    context.lineWidth = event.target.value || 1;
+                  });
+                
+                  // Mouse Down Event
+                  canvas.addEventListener('mousedown', function(event) {
+                    setMouseCoordinates(event);
+                    isDrawing = true;
+                
+                    // Start Drawing
+                    context.beginPath();
+                    context.moveTo(mouseX, mouseY);
+                  });
+                
+                  // Mouse Move Event
+                  canvas.addEventListener('mousemove', function(event) {
+                    setMouseCoordinates(event);
+                
+                    if(isDrawing){
+                      context.lineTo(mouseX, mouseY);
+                      context.stroke();
+                    }
+                  });
+                
+                  // Mouse Up Event
+                  canvas.addEventListener('mouseup', function(event) {
+                    setMouseCoordinates(event);
+                    isDrawing = false;
+                  });
+                
+                  // Handle Mouse Coordinates
+                  function setMouseCoordinates(event) {
+                    mouseX = event.clientX - boundings.left;
+                    mouseY = event.clientY - boundings.top;
+                  }
+                
+                  // Handle Clear Button
+                  var clearButton = document.getElementById('clear');
+                
+                  clearButton.addEventListener('click', function() {
+                    context.clearRect(0, 0, canvas.width, canvas.height);
+                  });
+                
+                  // Handle Save Button
+                  var saveButton = document.getElementById('save');
+                
+                  saveButton.addEventListener('click', function() {
+                    var uid = ao_module_utils.getRandomUID();
+                    if (ao_module_virtualDesktop){
+                        ao_module_openFileSelector(uid,"startSaveProcess",undefined,undefined,false,"folder");
+                    }else{
+                        ao_module_openFileSelectorTab(uid,"../../",false,"folder",startSaveProcess);
+                    }
+                    
+                  });
+                  
+                  
+                };
+                
+                function startSaveProcess(fileData){
+                    result = JSON.parse(fileData);
+                	for (var i=0; i < result.length; i++){
+                		var filename = result[i].filename;
+                		var filepath = result[i].filepath;
+                    	var imageName = prompt('Please enter image filename');
+                    	if (imageName == null){
+                    	    //Operation cancelled
+                    	    return;
+                    	}else if (imageName == ""){
+                    	    imageName = "untitled.png";
+                    	}
+                    	if (imageName.includes(".") == false){
+                    	    imageName = imageName + ".png";
+                    	}
+                    	var savePath = filepath + "/" + imageName;
+                        var canvasDataURL = canvas.toDataURL();
+                        console.log(canvasDataURL);
+                        $.post("paint.php",{"saveImage":canvasDataURL, "savePath": filepath + "/", "filename":imageName}).done(function(data){
+                            if(data.includes("ERROR") == false){
+                                //Successfully saved.
+                                ao_module_msgbox("Image file saved with path: " + savePath,"<i class='checkmark icon'></i> Paint File Saved");
+                            }else{
+                                alert(data);
+                            }
+                        });
+                        //window.open(canvasDataURL);
+                   }
+                }
+
+            </script>
+    </body>
+</html>
+

BIN
src/SystemAOB/utilities/sysicon/paint.png


+ 6 - 1
src/function_bar.js

@@ -1821,7 +1821,12 @@ function msgbox(warningMsg,title="",redirectpath="",autoclose=true){
 	}
 	box += warningMsg;
 	if (redirectpath != ""){
-		box += '<br><a style="cursor:pointer;" href="' + redirectpath + '" target="_blank">Open in Module <i class="external icon"></i></a>';
+		if (redirectpath == "{reload}"){
+			box += '<br><a style="cursor:pointer;" onClick="window.location.reload();">Refresh <i class="refresh icon"></i></a>';
+		}else{
+			box += '<br><a style="cursor:pointer;" href="' + redirectpath + '" target="_blank">Open in Module <i class="external icon"></i></a>';
+		}
+		
 	}
 	box += '</div><div class="four wide column" align="right"><i class="remove icon pressable" onClick="closemsgbox(this);"></i><br><br></div></div></div>';
 	$(box).hide().prependTo("#messageBoard").slideDown();

BIN
src/img/auth_icon_hk.png


BIN
src/img/background4.png