Kaynağa Gözat

Added share feature on desktop, optimized open with selections

TC pushbot 5 4 yıl önce
ebeveyn
işleme
0184c9e245

+ 1 - 0
file_system.go

@@ -167,6 +167,7 @@ func FileSystemInit() {
 	router.HandleFunc("/system/file_system/share/new", shareManager.HandleCreateNewShare)
 	router.HandleFunc("/system/file_system/share/delete", shareManager.HandleDeleteShare)
 	router.HandleFunc("/system/file_system/share/edit", shareManager.HandleEditShare)
+	router.HandleFunc("/system/file_system/share/checkShared", shareManager.HandleShareCheck)
 
 	//Handle the main share function
 	http.HandleFunc("/share", shareManager.HandleShareAccess)

+ 50 - 0
mod/share/share.go

@@ -476,6 +476,56 @@ func (s *Manager) HandleShareAccess(w http.ResponseWriter, r *http.Request) {
 
 }
 
+//Check if a file is shared
+func (s *Manager) HandleShareCheck(w http.ResponseWriter, r *http.Request) {
+	//Get the vpath from paramters
+	vpath, err := mv(r, "path", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid path given")
+		return
+	}
+
+	//Get userinfo
+	userinfo, err := s.options.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		sendErrorResponse(w, "User not logged in")
+		return
+	}
+
+	//Get realpath from userinfo
+	rpath, err := userinfo.VirtualPathToRealPath(vpath)
+	if err != nil {
+		sendErrorResponse(w, "Unable to resolve realpath")
+		return
+	}
+
+	type Result struct {
+		IsShared  bool
+		ShareUUID *ShareOption
+	}
+
+	//Check if share exists
+	shareExists := s.FileIsShared(rpath)
+	if !shareExists {
+		//Share not exists
+		js, _ := json.Marshal(Result{
+			IsShared:  false,
+			ShareUUID: &ShareOption{},
+		})
+		sendJSONResponse(w, string(js))
+
+	} else {
+		//Share exists
+		thisSharedInfo := s.GetShareObjectFromRealPath(rpath)
+		js, _ := json.Marshal(Result{
+			IsShared:  true,
+			ShareUUID: thisSharedInfo,
+		})
+		sendJSONResponse(w, string(js))
+	}
+
+}
+
 //Create new share from the given path
 func (s *Manager) HandleCreateNewShare(w http.ResponseWriter, r *http.Request) {
 	//Get the vpath from paramters

+ 4 - 4
web/PDF Viewer/init.agi

@@ -16,10 +16,10 @@ var moduleLaunchInfo = {
 	IconPath: "PDF Viewer/icons/module_icon.png",
 	Version: "2.7.570",
 	StartDir: "",
-	SupportFW: true,
-	LaunchFWDir: "PDF Viewer/viewer.html",
-	SupportEmb: false,
-	InitFWSize: [1024, 768],
+	SupportFW: false,
+	LaunchEmb: "PDF Viewer/viewer.html",
+	SupportEmb: true,
+	InitEmbSize: [1024, 768],
 	SupportedExt: [".pdf"]
 }
 

+ 1 - 1
web/SystemAO/file_system/file_explorer.html

@@ -683,7 +683,7 @@
              </div>
              <div class="mobilePathDisplayWrapper">
                 <div style="width:100%;" class="ts pathDisplay breadcrumb whiteTheme addressText mobileOnly">
-                    <div class="section wh onclick="refreshList();"iteTheme selectable"><i class="folder icon"></i> user</div>
+                    <div class="section whiteTheme selectable"><i class="folder icon"></i> user</div>
                     <div class="divider whiteTheme">:/</div>
                     <div class="section whiteTheme selectable">Desktop</div>
                 </div>

+ 372 - 0
web/SystemAO/file_system/file_share.html

@@ -0,0 +1,372 @@
+<html>
+    <head>
+        <title>File Share</title>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
+        <style>
+            body{
+                background: rgba(255,255,255,01) !important;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="ui stackable grid">
+            <div class="eight wide column">
+                <div class="width: 100%; ">
+                    <div style="display: block; margin-left: auto; margin-right: auto;" align="center">
+                        <div id="qrcode" style="border: 10px solid white; background-color: white;">
+                            <h1>Loading</h1>
+                        </div>
+                    </div>
+                    <div style="width: 100%">
+                        <a id="sharelink" href="" target="_blank" style="margin-top:8px; font-size: 120%; padding-left: 20px; padding-right: 20px; word-break: break-all; overflow-wrap: anywhere;">Scan this QR Code to copy the share link</a>
+                    </div>
+                    
+                </div>
+            </div>
+            <div id="shareSettingOptions" class="eight wide column" style="padding-left: 12px; display:none;">
+                <div class="ui header whiteTheme">
+                    Share Settings
+                    <div class="sub header whiteTheme">Change who can see this file</div>
+                </div>
+                <div id="shareSettingForm" class="ui form">
+                    <div class="field">
+                        <label><p class="whiteTheme">Visable options:</p></label>
+                        <div class="ui checkboxes">
+                            <div class="ui radio checkbox">
+                                <input id="anyone" type="radio" class="shareoption" name="shareopt" value="anyone" onchange="updateSharePermission(this);">
+                                <label for="anyone">
+                                    <div class="ui header">
+                                        <div class="content whiteTheme">
+                                            <i class="globe icon"></i> Anyone with the link
+                                            <div class="sub header whiteTheme">Anyone who has the link can access this file</div>
+                                        </div>
+                                    </div>
+                                </label>
+                            </div>
+                            <br><br>
+                            <div class="ui radio checkbox">
+                                <input id="signedin" type="radio" class="shareoption" value="signedin" name="shareopt" onchange="updateSharePermission(this);">
+                                <label for="signedin">
+                                    <div class="ui header">
+                                        <div class="content whiteTheme">
+                                            <i class="user circle outline icon"></i> Anyone signed in
+                                            <div class="sub header whiteTheme">Anyone who has signed in can access this file</div>
+                                        </div>
+                                    </div>
+                                </label>
+                            </div>
+                            <br><br>
+                            <div class="ui radio checkbox">
+                                <input id="samegroup" type="radio" class="shareoption" value="samegroup" name="shareopt" onchange="updateSharePermission(this);">
+                                <label for="samegroup">
+                                    <div class="ui header">
+                                        <div class="content whiteTheme">
+                                            <i class="users icon"></i> Users in the same group
+                                            <div class="sub header whiteTheme">Anyone who is in the same group as you do</div>
+                                        </div>
+                                    </div>
+                                </label>
+                            </div>
+                        <br><br>
+                        <div id="udpateNotification" style="display:none;" class="ui green inverted segment">
+                                <i class=" checkmark icon"></i> Share Setting Updated
+                        </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="ui divider"></div>
+        <div style="width: 100%; padding-right: 12px;" align="right">
+            <div class="ui button popupbuttons whiteTheme allowHover" onclick="copyLinkToClipboard(this)">
+                <i class="copy icon"></i> Copy
+            </div> 
+            <div id="sharingRemoveBtn" class="ui red button popupbuttons negative whiteTheme allowHover" onclick="removeSharing()">
+                <i class="remove icon"></i> Remove
+            </div> 
+        </div>
+        <script>
+            /*
+                Usage: Pass in file descriptor to start share a file
+                var fd = encodeURIComponent(JSON.stringify({
+                        filename: "test.txt",
+                        filepath: "user:/Desktop/test.txt"
+                    })
+                );
+                window.open("file_share.html#" + fd);
+                
+                If you want to preset a file sharing mode (aka your module handle the share mode picking),
+                Pass in with the "shareMode" paramter. Example fd as follow:
+                {
+                    filename: "test.txt",
+                    filepath: "user:/Desktop/test.txt",
+                    shareMode: "signedin"
+                }
+
+                To remove a share, pass in "remove" for the share mode
+                Supported Share Mode keywords {anyone/signedin/samegroup/remove}
+
+            */
+            var shareEditingUUID = "";
+            var shareingFileData = {};
+            var initialized = false;
+            var fileSharingURL = "";
+
+            function PageReady(){
+                if (initialized){
+                    return;
+                }
+                initialized = true;
+                $(".checkbox").checkbox();
+                var inputFile = ao_module_loadInputFiles();
+                if (inputFile == null){
+                    //No file selected
+                    $(".shareoption").parent().addClass("disabled");
+                    $("#qrcode").html(`<h1>No File</h1>`);
+                    $("#sharelink").text(``);
+                    $("#sharingRemoveBtn").addClass("disabled");
+                    return
+                }
+                //Make sure one file is choicen each time
+                inputFile = inputFile[0]; 
+                shareingFileData = inputFile;
+                initFileDetails(shareingFileData, function(shareUUID){
+                    //Set the mode of share if it is defined
+                    if (shareingFileData.shareMode !== undefined && shareingFileData.shareMode == "remove"){
+                        //Remove the share UUID
+                        removeSharing();
+                        return;
+                    }
+                    if (shareingFileData.shareMode !== undefined){
+                        //As the share mode is defined by the caller, hide the setting interface
+                        $("#shareSettingOptions").hide();
+                        $.ajax({
+                            url: "../../system/file_system/share/edit",
+                            data: {uuid: shareEditingUUID, mode: shareingFileData.shareMode},
+                            success: function(data){
+                                if (data.error !== undefined){
+                                    alert(data.error);
+                                    return;
+                                }
+                                
+                                //Update the checkbox
+                                $(".shareoption").each(function(){
+                                    if ($(this)[0].value != shareingFileData.shareMode){
+                                        $(this)[0].checked = false;
+                                    }else{
+                                        $(this)[0].checked = true;
+                                    }
+                                
+                                });
+                            }
+                        });
+                    }else{
+                        //Default: show the setting to allow user adjustment
+                        $("#shareSettingOptions").show();
+                    }
+                });
+            }
+
+            function initFileDetails(shareingFileData, callback=undefined){
+                $.ajax({
+                    url: "../../system/file_system/share/new",
+                    data: {path: shareingFileData.filepath},
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            updateShareLinkInfo(data.UUID);
+                            shareEditingUUID = data.UUID;
+                            $(".shareoption").each(function(){
+                                if ($(this)[0].value != data.Permission){
+                                    $(this)[0].checked = false;
+                                }else{
+                                    $(this)[0].checked = true;
+                                }
+                               
+                            });
+
+                            if (callback != undefined){
+                                callback(data.UUID)
+                            }
+                        }
+                    }
+                });
+            }
+
+            function removeSharing(){
+                if (shareEditingUUID == ""){
+                    return
+                }
+
+                //The target file to remove
+                $.ajax({
+                    url: "../../system/file_system/share/delete",
+                    data: {path: shareingFileData.filepath},
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            //Removed!
+                            $(".button").addClass('disabled');
+                            $(".checkbox").addClass("disabled");
+                            $("#sharelink").text("");
+                            $("#sharelink").attr("href", "#");
+                            $("#qrcode").html(`<br><br><h1><i class="green checkmark icon"></i> Share Removed</h1>`);
+
+                        }
+                    }
+                });
+            }
+
+            function updateSharePermission(object){
+                var newPermission = $(object).attr('value');
+                $.ajax({
+                    url: "../../system/file_system/share/edit",
+                    data: {uuid: shareEditingUUID, mode: newPermission},
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                            return;
+                        }
+                        $("#udpateNotification").slideDown("fast").delay(3000).slideUp("fast");
+                    }
+                });
+            }   
+
+            function updateShareLinkInfo(uuid){
+                $("#qrcode").html("");
+                let protocol = "https://";
+                if (location.protocol !== 'https:') {
+                    protocol = "http://";
+                }
+                var shareURL = protocol + window.location.hostname + ":" + window.location.port + "/share?id=" + uuid;
+                shareEditingUUID = uuid;
+                fileSharingURL = shareURL;
+                new QRCode(document.getElementById("qrcode"), shareURL);
+                $("#sharelink").text(shareURL);
+                $("#sharelink").attr("href", shareURL)
+            }
+
+            /*
+                Dynamic Script Loader
+
+                This is a really experimental implementation of importing a script from anywhere
+                under the ArozOS web root. This section of code must be written in plain JS to make sure
+                it works without jQuery and other libraries.
+
+                This function try to load jQuery and ao_module from the script folder.
+                Also loading the semantic js and the css main body
+            */
+            //The possible location for desktop.system, standard webapp module, SystemAO interfaces and iui sub-interfaces
+            let possibleLocations = ["script/", "../script/", "../../script/", "../../../script/"];
+            let loopCount = Math.min(possibleLocations.length, JSON.parse(JSON.stringify(window.location.toString())).split("/").length - 3);
+            function tryLoad(relpath, filename, successCallback=undefined){
+                var request = new XMLHttpRequest();  
+                request.open('GET', relpath + filename, true);
+                request.onreadystatechange = function(){
+                    if (request.readyState === 4){
+                        if (request.status == 200 || request.status == 304) {  
+                            if (typeof(successCallback) != "undefined"){
+                                successCallback(relpath, filename);
+                            }
+                        }
+                    }
+                };
+                request.send();
+            }
+
+            function injectOtherJavaScriptLibrary(relpath){
+                //Check if ao_module is loaded
+                if (typeof(ao_module_virtualDesktop) == "undefined"){
+                    var script = document.createElement('script');
+                    script.setAttribute('src', relpath + "ao_module.js");
+                    document.getElementsByTagName('head')[0].appendChild(script);
+                }else{
+                    //This routine already run
+                    return;
+                }
+
+                //Inject QR Code library
+                var script = document.createElement('script');
+                script.setAttribute('src', relpath + "qrcode.min.js");
+                document.getElementsByTagName('head')[0].appendChild(script);
+
+                //Inject semmantic css and js anyway
+                var head  = document.getElementsByTagName('head')[0];
+                var link  = document.createElement('link');
+                link.id   = "semantic";
+                link.rel  = 'stylesheet';
+                link.type = 'text/css';
+                link.href = relpath + 'semantic/semantic.min.css';
+                link.media = 'all';
+                head.appendChild(link);
+
+                var script = document.createElement('script');
+                script.setAttribute('src', relpath + "semantic/semantic.min.js");
+                document.getElementsByTagName('head')[0].appendChild(script);
+
+                setTimeout(function(){
+                    PageReady();
+                }, 1000);
+            }
+
+            //Load jQuery first
+            if (typeof(window.jQuery) == "undefined"){
+                //jQuery not found. Laod it
+                for (var i = 0; i < loopCount; i++){
+                    var relpath = possibleLocations[i];
+                    tryLoad(relpath, "jquery.min.js", function(relpath, filename){
+                        //Generate the jquery script element
+                        var script = document.createElement('script');
+                        
+                        script.setAttribute('src', relpath + filename);
+                        document.getElementsByTagName('head')[0].appendChild(script);
+                        
+                        doAfterJqueryLoaded(function(){
+                            injectOtherJavaScriptLibrary(relpath);
+                        });
+                        
+                        
+                    });
+                }
+            }else{
+                //jQuery exists. Load ao_module
+                dynamicLoadAoModule();
+            }
+
+            function doAfterJqueryLoaded(callback){
+                setTimeout(function(){
+                    //Wait until it is loaded
+                    if (typeof(window.jQuery) == "undefined"){
+                        doAfterJqueryLoaded(callback);
+                        return
+                    }else{
+                        callback();
+                    }
+                }), 300;
+            }
+
+            function copyLinkToClipboard(btn){
+                //Copy text
+                const area = document.createElement('textarea');
+                document.body.appendChild(area);
+
+                area.value = fileSharingURL;
+                area.select();
+                document.execCommand('copy');
+
+                document.body.removeChild(area);
+
+                //Do visual feedback
+                let oldContent = $(btn).html();
+                $(btn).html(`<i class="green checkmark icon"></i> Copied!`);
+                setTimeout(function(){
+                    $(btn).html(oldContent);
+                }, 3000);
+            }
+
+        </script>
+    </body>
+</html>

BIN
web/SystemAO/file_system/img/baseline_hourglass_empty_black_48dp.png


+ 1 - 0
web/SystemAO/file_system/img/share.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg>

+ 0 - 18
web/SystemAO/file_system/upload_manager.html

@@ -1,18 +0,0 @@
-<html>
-    <head>
-        <title>Upload Manager</title>
-        <meta charset="UTF-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
-        <link rel="stylesheet" href="../../script/tocas/tocas.css">
-        <script type="text/javascript" src="../../script/tocas/tocas.js"></script>
-        <script type="text/javascript" src="../../script/jquery.min.js"></script>
-        <script type="text/javascript" src="../../script/ao_module.js"></script>
-    </head>
-    <body>
-        
-        <script>
-            var targetDir = ao_module_loadInputFiles();
-            console.log(targetDir);
-        </script>
-    </body>
-</html>

+ 273 - 10
web/desktop.system

@@ -10,7 +10,6 @@
     <script type="text/javascript" src="script/tocas/tocas.js"></script>
     <script type="text/javascript" src="script/jquery.min.js"></script>
     <script type="text/javascript" src="script/ao_module.js"></script>
-    <script type="text/javascript" src="SystemAO/desktop/offline/pong.js"></script>
     <script type="text/javascript" src="SystemAO/desktop/script/jsCalendar/source/jsCalendar.js"></script>
     <style>
         body {
@@ -3243,7 +3242,7 @@
                                 title += " file";
                             }
 
-                            parent.newFloatWindow({
+                            newFloatWindow({
                                 url: "SystemAO/file_system/file_operation.html#" + configHash,
                                 width: 400,
                                 height: 220,
@@ -3962,7 +3961,7 @@
                             //When the user selected more than one item on desktop
                             addContextMenuItem($("#contextmenu"), 'Open', undefined, "openIconViaMenu", false);
                             addContextMenuItem($("#contextmenu"), 'Open With', "<i class='caret right icon'></i>", "handleOpenWith", true);
-                            addContextMenuItem($("#contextmenu"), 'Share', "<i class='caret right icon'></i>", "handleFileShare", true);
+                            //addContextMenuItem($("#contextmenu"), 'Share', "<i class='caret right icon'></i>", "handleFileShare", true);
                             addContextMenuSeperator($("#contextmenu"));
                             addContextMenuItem($("#contextmenu"), 'Download in Zip', "<i class='download icon'></i>", "downloadFile", false);
                             addContextMenuItem($("#contextmenu"), 'Make a Copy', "<i class='copy icon'></i>", "handleCloning", false);
@@ -4192,6 +4191,15 @@
 
         function handleOpenWith(target, event){
             $("#subcontextmenu").html("");
+            //Updates 19 May 2021: Added support for better parsing Open With List
+            var exts = [];
+            //Get all exts from the selected files
+            $(".launchIconWrapper.selected").each(function(){
+                var filename = $(this).parent().attr("filename");
+                var ext = "." + filename.split(".").pop();
+                exts.push(ext);
+            });
+
             $.get("system/modules/list",function(data){
                 if (data.error !== undefined){
                     alert(data.error);
@@ -4200,16 +4208,223 @@
                     var openWithTargets = [];
                     for (var i =0; i < data.length; i++){
                         var thisModule = data[i];
-                        if (thisModule.SupportEmb == true){
+
+                        //If this module support this ext, add it to list
+                        var thisModuleSupportExt = thisModule.SupportedExt;
+                        if (thisModule.Name == "File Manager"){
+                            //Exceptional case
                             addContextMenuItem($("#subcontextmenu"), thisModule.Name, "", "openSelectedWith", false);
                         }
+                        if (thisModuleSupportExt == null || thisModuleSupportExt.length == 0){
+                            //Not supporting anything
+                            continue;
+                        }
+                        thisModuleSupportExt.forEach(supportedExt => {
+                            if (exts.includes(supportedExt)){
+                                //Add this to the menu
+                                addContextMenuItem($("#subcontextmenu"), thisModule.Name, "", "openSelectedWith", false);
+                            }
+                        })
                         
+
+                        //if (thisModule.SupportEmb == true){
+                        //    addContextMenuItem($("#subcontextmenu"), thisModule.Name, "", "openSelectedWith", false);
+                        //}
                     }
+
+                    //Add select more opener option
+                    addContextMenuSeperator($("#subcontextmenu"));
+                    addContextMenuItem($("#subcontextmenu"), "Select Other WebApps", ``, "openOpenSelector", false);
                 }
                 showSubContextMenu(target);
             });
         }
 
+        function handleFileShare(target, object){
+            $("#subcontextmenu").html("");
+            //Get the selected file
+            var targetFile = $(".launchIconWrapper.selected")[0];
+            var fileData = $(targetFile).parent().attr("filedata");
+            if (fileData != undefined){
+                fileData = JSON.parse(decodeURIComponent(fileData));
+                 //Check if the target file is shared
+                 $.ajax({
+                     url: "system/file_system/share/checkShared",
+                     data: {path: fileData.Filepath},
+                     success: function(data){
+                        if (data.IsShared == true){
+                            addContextMenuItem($("#subcontextmenu"), "Open Shared Link", `<i class="external icon"></i>`, "openSharedLink", false, {
+                                payload: {
+                                    uuid: data.ShareUUID.UUID
+                                }
+                            });
+                            addContextMenuItem($("#subcontextmenu"), "Copy Link", `<i class="copy icon"></i>`, "copySharedLink", false, {
+                                payload: {
+                                    uuid: data.ShareUUID.UUID
+                                }
+                            });
+                            addContextMenuSeperator($("#subcontextmenu"));
+                            addContextMenuTitle($("#subcontextmenu"), `<i class="share alternate icon"></i> Change Share Settings`);
+                        }else{
+                            addContextMenuTitle($("#subcontextmenu"), `<i class="share alternate icon"></i> Share this File`);
+                        }
+                        console.log(data);
+
+                        var checkmarkStyle = `style="color: #71ba68;"`;
+                        var anyoneClass = "";
+                        if (data.ShareUUID.Permission == "anyone"){
+                            anyoneClass = `<i ${checkmarkStyle} class="checkmark icon"></i>`;
+                        }
+                        var signedinClass = "";
+                        if (data.ShareUUID.Permission == "signedin"){
+                            signedinClass = `<i ${checkmarkStyle} class="checkmark icon"></i>`;
+                        }
+
+                        var sameGroupClass = "";
+                        if (data.ShareUUID.Permission == "samegroup"){
+                            samegroup = `<i ${checkmarkStyle} class="checkmark icon"></i>`;
+                        }
+
+                        addContextMenuItem($("#subcontextmenu"), `<i class="globe icon"></i> Anyone with Link`, anyoneClass, "shareWithThisPermission", false, {
+                            payload: {
+                                type: "anyone"
+                            }
+                        });
+                        addContextMenuItem($("#subcontextmenu"), `<i class="user circle outline icon"></i> Anyone Signed In`, signedinClass, "shareWithThisPermission", false, {
+                            payload: {
+                                type: "signedin"
+                            }
+                        });
+                        addContextMenuItem($("#subcontextmenu"), `<i class="users icon"></i> Same Group Users`, sameGroupClass, "shareWithThisPermission", false,{
+                            payload: {
+                                type: "samegroup"
+                            }
+                        });
+                        if (data.IsShared == true){
+                            //This file is shared.
+                            addContextMenuSeperator($("#subcontextmenu"));
+                            addContextMenuItem($("#subcontextmenu"), "Remove Share", `<i style="color: #d97762;" class="trash icon"></i>`, "shareWithThisPermission", false,{
+                                payload: {
+                                    type: "remove"
+                                }
+                            });
+                        }
+                     }
+                 });
+            }
+            
+            showSubContextMenu(target);
+        }
+
+        //Copy the shared link
+        function copySharedLink(target, event){
+            var payload = getContextMenuPayload(target);
+            if (payload == null){
+                //Something wrong
+                return;
+            }
+
+            //Generate the share link
+            let protocol = "https://";
+            if (location.protocol !== 'https:') {
+                protocol = "http://";
+            }
+            var shareURL = protocol + window.location.hostname + ":" + window.location.port + "/share?id=" + payload.uuid;
+
+            //Copy the text to clipboard
+            const area = document.createElement('textarea');
+            document.body.appendChild(area);
+
+            area.value = shareURL;
+            area.select();
+            document.execCommand('copy');
+
+            document.body.removeChild(area);
+
+            //Update status so it show the user it has been coped
+            $(target).html(`<i style="color: #71ba68;" class="checkmark icon"></i> Copied!`);
+            setTimeout(function(){
+                if ($(target).length > 0){
+                    $(target).html(`Copy Link
+                        <span class="description">
+                            <i class="copy icon"></i>
+                        </span>`);
+                }
+            }, 3000);
+        }
+
+        //Open the shared file link
+        function openSharedLink(target, event){
+            var payload = getContextMenuPayload(target);
+            if (payload == null){
+                //Something wrong
+                return;
+            }
+
+            var targetURL = "share?id=" + payload.uuid;
+            window.open(targetURL);
+        }
+
+        function shareWithThisPermission(target, event){
+            var payload = getContextMenuPayload(target);
+            if (payload == null){
+                //Something wrong
+                return;
+            }
+
+            //Get the selected file
+            var selectedFileObjects = $(".launchIconWrapper.selected");
+            if (selectedFileObjects.length == 0){
+                //Not found
+                return
+            }else if (selectedFileObjects.length > 1){
+                //This should not happens. If it did happens, use the first one
+                selectedFileObjects = selectedFileObjects[0];
+            }
+
+            //Extract file information
+            var fileData = $(selectedFileObjects).parent().attr("filedata");
+            fileData = JSON.parse(decodeURIComponent(fileData));
+            
+            //Convert the desktop file data to ArozOS generic filedata
+            var afd = {
+                "filename": fileData.Filename,
+                "filepath": fileData.Filepath,
+                "shareMode": payload.type
+            };
+
+            //Create a new share for this
+            var targetURL = "SystemAO/file_system/file_share.html#" + encodeURIComponent(JSON.stringify([afd]));
+
+
+            //Calculate the window spawn position
+            var basePosition = $(selectedFileObjects).offset();
+            var left = basePosition.left + 100;
+            if (left + 320 > window.innerWidth){
+                left = basePosition.left - 350;
+            }
+
+            var top = basePosition.top;
+            if (top + 480 > window.innerHeight){
+                top = window.innerHeight - 480;
+            }
+
+            newFloatWindow({
+                url: targetURL,
+                width: 330,
+                height: 480,
+                left: left,
+                top: top,
+                appicon: "SystemAO/file_system/img/share.svg",
+                title: "Share File"
+            }, function(windowUUID){
+                //Set the window to fixed window
+                setFloatWindowResizePolicy(windowUUID, false);
+            });
+
+            hideAllContextMenus();
+        }
+
         function openObjectProperty(target, object){
            var filelist = [];
            $(".launchIconWrapper.selected").each(function(){
@@ -4229,6 +4444,27 @@
             hideAllContextMenus();
         }
 
+        //Open the file extension selector
+        function openOpenSelector(target, event){
+            var filedata = $($(".launchIconWrapper.selected")[0]).parent().attr("filedata")
+            filedata = JSON.parse(decodeURIComponent(filedata));
+            
+            var fileDescriptor = {
+                filepath: filedata.Filepath,
+                filename: filedata.Filename
+            }
+            
+            newFloatWindow({
+                url: "SystemAO/file_system/defaultOpener.html#" + encodeURIComponent(JSON.stringify(fileDescriptor)),
+                width: 320,
+                height: 510,
+                appicon: "SystemAO/file_system/img/opener.png",
+                title: "Select WebApp to Open File",
+            });
+
+            hideAllContextMenus();
+        }
+
         function openSelectedWith(target, event){
             var targetModulename = $(target).text().trim();
             var openFileList = [];
@@ -4250,9 +4486,7 @@
 
            //Open the files with the given module
             openFileWithModule(targetModuleInfo, openFileList);
-
             hideAllContextMenus();
-
         }
 
         function getModuleInfoFromName(moduleName){
@@ -4265,25 +4499,48 @@
             return false
         }
 
+        /*
+            This function is used to generate menu items for context menu.
+            Options example
+            {
+                payload: {
+                    //Some JSON based payload here
+                }
+            }
+        */
 
-
-        function addContextMenuItem(target, keyword, hotkey = undefined, functionName, containSubMenu = false) {
+        function addContextMenuItem(target, keyword, hotkey = undefined, functionName, containSubMenu = false, options=undefined) {
             var itemClass = "item";
             if (containSubMenu) {
                 itemClass = "item submenu";
             }
+
+            var payload = "";
+            if (options != undefined && options.payload != undefined){ 
+                payload = encodeURIComponent(JSON.stringify(options.payload));
+            }
+
             if (hotkey !== undefined) {
-                target.append(`<div class="${itemClass}" onclick="${functionName}(this,event);">
+                target.append(`<div class="${itemClass}" onclick="${functionName}(this,event);" payload="${payload}">
                         ${keyword}
                         <span class="description">${hotkey}</span>
                     </div>`);
             } else {
-                target.append(`<div class="${itemClass}"  onclick="${functionName}(this,event);">
+                target.append(`<div class="${itemClass}"  onclick="${functionName}(this,event);" payload="${payload}">
                         ${keyword}
                     </div>`);
             }
         }
 
+        function getContextMenuPayload(object){
+            var encodedPayload = $(object).attr("payload");
+            if (encodedPayload != ""){
+                return JSON.parse(decodeURIComponent(encodedPayload));
+            }else{
+                return null;
+            }
+        }
+
         function listIconsize(target, event) {
             $("#subcontextmenu").html("");
             var smallCheckmark = undefined;
@@ -4319,6 +4576,12 @@
             $(target).append(`<div class="divider"></div>`);
         }
 
+        function addContextMenuTitle(target, title){
+            $(target).append(`<div class="header">
+                ${title}
+            </div>`);
+        }
+
         function downloadURL() {
             alert("WIP");
             hideAllContextMenus();