diff --git a/d.ts/antos.d.ts b/d.ts/antos.d.ts index 2da83df..df8e7b1 100644 --- a/d.ts/antos.d.ts +++ b/d.ts/antos.d.ts @@ -4029,6 +4029,14 @@ declare namespace OS { * @param {VFSFileHandleClass} cls handle class */ function register(protos: string, cls: VFSFileHandleClass): void; + /** + * Load custom VFS handles if the package vfsx available + * + * @export + * @param {boolean} [force] force load the file + * @return {*} {Promise} + */ + function loadVFSX(force?: boolean): Promise; /** * Looking for a attached file handle class of a string protocol * diff --git a/release/antos-1.2.1.tar.gz b/release/antos-1.2.1.tar.gz index 2c8db67..4bfb714 100644 Binary files a/release/antos-1.2.1.tar.gz and b/release/antos-1.2.1.tar.gz differ diff --git a/src/core/gui.ts b/src/core/gui.ts index fbfe03b..460f518 100644 --- a/src/core/gui.ts +++ b/src/core/gui.ts @@ -1172,20 +1172,24 @@ namespace OS { ? result : undefined; } - + // load VFSX // GUI.refreshSystemMenu() // GUI.buildSystemMenu() // push startup services // TODO: get services list from user setting - pushServices( - (() => { - const result = []; - for (let v of setting.system.startup.services) { - result.push(v); - } - return result; - })() - ).then(function () { + Promise.all( + [OS.API.VFS.loadVFSX(true), + pushServices( + (() => { + const result = []; + for (let v of setting.system.startup.services) { + result.push(v); + } + return result; + })() + ) + ]) + .then(function () { setting.system.startup.apps.map((a) => { launch(a, []); }); diff --git a/src/core/vfs.ts b/src/core/vfs.ts index 63d7014..52b6ba7 100644 --- a/src/core/vfs.ts +++ b/src/core/vfs.ts @@ -165,7 +165,9 @@ namespace OS { */ export namespace VFS { declare var JSZip: any; - + /** path for custom VFS protocol handles */ + const VFSX_PATH = "pkg://vfsx/vfsx.js"; + String.prototype.asFileHandle = function (): BaseFileHandle { const list = this.split("://"); const handles = API.VFS.findHandles(list[0]); @@ -198,7 +200,27 @@ namespace OS { ): void { handles[protos] = cls; } - + /** + * Load custom VFS handles if the package vfsx available + * + * @export + * @param {boolean} [force] force load the file + * @return {*} {Promise} + */ + export function loadVFSX(force?: boolean): Promise + { + return new Promise(async (resolve, reject) => { + try { + await API.requires(VFSX_PATH, force); + resolve(true); + } + catch(e) + { + console.warn("No custom VFS protocol loaded"); + resolve(true); + } + }) + } /** * Looking for a attached file handle class of a string protocol * diff --git a/src/core/vfs/GoogleDriveHandle.js b/src/core/vfs/GoogleDriveHandle.js deleted file mode 100644 index ee0cb67..0000000 --- a/src/core/vfs/GoogleDriveHandle.js +++ /dev/null @@ -1,401 +0,0 @@ -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS202: Simplify dynamic range loops - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ - -// Copyright 2017-2018 Xuan Sang LE - -// AnTOS Web desktop is is licensed under the GNU General Public -// License v3.0, see the LICENCE file for more information - -// This program is free software: you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation, either version 3 of -// the License, or (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. - -// You should have received a copy of the GNU General Public License -//along with this program. If not, see https://www.gnu.org/licenses/. - -// GoogleDrive File Handle -let G_CACHE = {"gdv://":{ id: "root", mime: 'dir' } }; - -class GoogleDriveHandle extends this.OS.API.VFS.BaseFileHandle { - constructor(path) { - super(path); - const me = this; - this.setting = Ant.OS.setting.VFS.gdrive; - if (!this.setting) { return Ant.OS.announcer.oserror(__("Unknown API setting for {0}", "GAPI"), (Ant.OS.API.throwe("OS.VFS")), null); } - if (this.isRoot()) { this.gid = 'root'; } - this.cache = ""; - } - - oninit(f) { - const me = this; - if (!this.setting) { return; } - const fn = function(r) { - if (r) { return f(); } - // perform the login - G_CACHE = {"gdv://":{ id: "root", mime: 'dir' } }; - return gapi.auth2.getAuthInstance().signIn(); - }; - - if (Ant.OS.API.libready(this.setting.apilink)) { - gapi.auth2.getAuthInstance().isSignedIn.listen(r => fn(r)); - return fn(gapi.auth2.getAuthInstance().isSignedIn.get()); - } else { - return Ant.OS.API.require(this.setting.apilink, function() { - // avoid popup block - const q = Ant.OS.announcer.getMID(); - return gapi.load("client:auth2", function() { - Ant.OS.API.loading(q, "GAPI"); - return gapi.client.init({ - apiKey: me.setting.API_KEY, - clientId: me.setting.CLIENT_ID, - discoveryDocs: me.setting.DISCOVERY_DOCS, - scope: me.setting.SCOPES - }) - .then(function() { - Ant.OS.API.loaded(q, "OK"); - gapi.auth2.getAuthInstance().isSignedIn.listen(r => fn(r)); - return _GUI.openDialog("YesNoDialog", function(d) { - if (!d) { return Ant.OS.announcer.osinfo(__("User abort the authentication")); } - return fn(gapi.auth2.getAuthInstance().isSignedIn.get()); - } - , __("Authentication") - , { text: __("Would you like to login to {0}?", "Google Drive") });}) - .catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot init {0}: {1}", "GAPI",err.error), (Ant.OS.API.throwe("OS.VFS")), err); - }); - }); - }); - } - } - - meta(f) { - const me = this; - return this.oninit(function() { - const q = Ant.OS.announcer.getMID(); - if (G_CACHE[me.path]) { me.gid = G_CACHE[me.path].id; } - if (me.gid) { - //console.log "Gid exists ", me.gid - Ant.OS.API.loading(q, "GAPI"); - return gapi.client.drive.files.get({ - fileId: me.gid, - fields: me.fields() - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (!r.result) { return; } - r.result.mime = r.result.mimeType; - return f(r);}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot get meta data for {0}", me.gid), (Ant.OS.API.throwe("OS.VFS")), err); - }); - } else { - //console.log "Find file in ", me.parent() - const fp = me.parent().asFileHandle(); - return fp.meta(function(d) { - const file = d.result; - const q1 = Ant.OS.announcer.getMID(); - Ant.OS.API.loading(q1, "GAPI"); - G_CACHE[fp.path] = { id: file.id, mime: file.mimeType }; - return gapi.client.drive.files.list({ - q: `name = '${me.basename}' and '${file.id}' in parents and trashed = false`, - fields: `files(${me.fields()})` - }) - .then(function(r) { - //console.log r - Ant.OS.API.loaded(q1, "OK"); - if (!r.result.files || !(r.result.files.length > 0)) { return; } - G_CACHE[me.path] = { id: r.result.files[0].id, mime: r.result.files[0].mimeType }; - r.result.files[0].mime = r.result.files[0].mimeType; - return f({ result: r.result.files[0] });}) - .catch(function(err) { - Ant.OS.API.loaded(q1, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot get meta data for {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - }); - } - }); - } - - fields() { - return "webContentLink, id, name,mimeType,description, kind, parents, properties, iconLink, createdTime, modifiedTime, owners, permissions, fullFileExtension, fileExtension, size"; - } - isFolder() { - return this.info.mimeType === "application/vnd.google-apps.folder"; - } - - save(id, m, f) { - const me = this; - const user = gapi.auth2.getAuthInstance().currentUser.get(); - const oauthToken = user.getAuthResponse().access_token; - const q = Ant.OS.announcer.getMID(); - const xhr = new XMLHttpRequest(); - const url = 'https://www.googleapis.com/upload/drive/v3/files/' + id + '?uploadType=media'; - xhr.open('PATCH', url); - xhr.setRequestHeader('Authorization', 'Bearer ' + oauthToken); - xhr.setRequestHeader('Content-Type', m); - xhr.setRequestHeader('Content-Encoding', 'base64'); - xhr.setRequestHeader('Content-Transfer-Encoding', 'base64'); - Ant.OS.API.loading(q, "GAPI"); - const error = function(e, s) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot save : {0}", me.path), e, s); - }; - xhr.onreadystatechange = function() { - if ( xhr.readyState === 4 ) { - if ( xhr.status === 200 ) { - Ant.OS.API.loaded(q, "OK"); - return f({ result: JSON.parse(xhr.responseText) }); - } else { - return error(xhr, xhr.status); - } - } - }; - xhr.onerror = () => error(xhr, xhr.status); - if (m === "base64") { return xhr.send(me.cache.replace(/^data:[^;]+;base64,/g, "")); } - return this.sendB64(m, data => xhr.send(data.replace(/^data:[^;]+;base64,/g, ""))); - } - - getlink() { - if (this.ready) { return this.info.webContentLink; } - return undefined; - } - - action(n, p, f) { - const me = this; - const q = Ant.OS.announcer.getMID(); - Ant.OS.API.loading(q, "GAPI"); - switch (n) { - case "read": - if (!this.info.id) { return; } - if (this.isFolder()) { - return gapi.client.drive.files.list({ - q: `'${me.info.id}' in parents and trashed = false`, - fields: `files(${me.fields()})` - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (!r.result.files) { return; } - for (let file of Array.from(r.result.files)) { - file.path = me.child(file.name); - file.mime = file.mimeType; - file.filename = file.name; - file.type = "file"; - file.gid = file.id; - if (file.mimeType === "application/vnd.google-apps.folder") { - file.mime = "dir"; - file.type = "dir"; - file.size = 0; - } - G_CACHE[file.path] = { id: file.gid, mime: file.mime }; - } - return f({ result: r.result.files });}) - .catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot read : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - } else { - return gapi.client.drive.files.get({ - fileId: me.info.id, - alt: 'media' - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (p !== "binary") { return f(r.body); } - return f(r.body.asUint8Array());}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot read : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - } - - case "mk": - if (!this.isFolder()) { return f({ error: __("{0} is not a directory", this.path) }); } - var meta = { - name: p, - parents: [this.info.id], - mimeType: 'application/vnd.google-apps.folder' - }; - - gapi.client.drive.files.create({ - resource: meta, - fields: 'id' - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - //console.log r - if (!r || !r.result) { return Ant.OS.announcer.oserror(__("VFS cannot create : {0}", p), (Ant.OS.API.throwe("OS.VFS")), r); } - G_CACHE[me.child(p)] = { id: r.result.id, mime: "dir" }; - return f(r);}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot create : {0}", p), (Ant.OS.API.throwe("OS.VFS")), err); - }); - - return; - - case "write": - var gid = undefined; - if (G_CACHE[me.path]) { gid = G_CACHE[me.path].id; } - if (gid) { - Ant.OS.API.loaded(q, "OK"); - return this.save(gid, p, f); - } else { - const dir = this.parent().asFileHandle(); - return dir.onready(function() { - meta = { - name: me.basename, - mimeType: p, - parents: [dir.info.id] - }; - - return gapi.client.drive.files.create({ - resource: meta, - fields: 'id' - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (!r || !r.result) { return Ant.OS.announcer.oserror(__("VFS cannot write : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), r); } - G_CACHE[me.path] = { id: r.result.id, mime: p }; - return me.save(r.result.id, p, f);}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot write : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - }); - } - - case "upload": - if (!this.isFolder()) { return; } - //insert a temporal file selector - var o = ($('')).attr('type', 'file').css("display", "none"); - Ant.OS.API.loaded(q, "OK"); - o.change(function() { - //Ant.OS.API.loading q, p - const fo = o[0].files[0]; - const file = (me.child(fo.name)).asFileHandle(); - file.cache = fo; - file.write(fo.type, f); - return o.remove(); - }); - - //Ant.OS.API.loaded q, p, "OK" - //Ant.OS.API.loaded q, p, "FAIL" - - return o.click(); - - case "remove": - if (!this.info.id) { return; } - return gapi.client.drive.files.delete({ - fileId: me.info.id - }) - .then(function(r) { - //console.log r - Ant.OS.API.loaded(q, "OK"); - if (!r) { return Ant.OS.announcer.oserror(__("VFS cannot delete : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), r); } - G_CACHE[me.path] = null; - return f({ result: true });}) - .catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot delete : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - - case "publish": - Ant.OS.API.loaded(q, "OK"); - return; - - case "download": - return gapi.client.drive.files.get({ - fileId: me.info.id, - alt: 'media' - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (!r.body) { return Ant.OS.announcer.oserror(__("VFS cannot download file : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), r); } - let bytes = []; - for (let i = 0, end = r.body.length - 1, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { - bytes.push(r.body.charCodeAt(i)); - } - bytes = new Uint8Array(bytes); - const blob = new Blob([bytes], { type: "octet/stream" }); - return Ant.OS.API.saveblob(me.basename, blob);}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot download file : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), err); - }); - - case "move": - var dest = p.asFileHandle().parent().asFileHandle(); - return dest.onready(function() { - const previousParents = me.info.parents.join(','); - return gapi.client.drive.files.update({ - fileId: me.info.id, - addParents: dest.info.id, - removeParents: previousParents, - fields: "id" - }) - .then(function(r) { - Ant.OS.API.loaded(q, "OK"); - if (!r) { return Ant.OS.announcer.oserror(__("VFS cannot move : {0}", me.path), (Ant.OS.API.throwe("OS.VFS")), r); } - return f(r);}).catch(function(err) { - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.oserror(__("VFS cannot move : {0}", me.gid), (Ant.OS.API.throwe("OS.VFS")), err); - }); - } - , err => Ant.OS.API.loaded(q, "FAIL")); - default: - Ant.OS.API.loaded(q, "FAIL"); - return Ant.OS.announcer.osfail(__("VFS unknown action: {0}", n), (Ant.OS.API.throwe("OS.VFS")), n); - } - } -} - - -self.OS.API.VFS.register("^gdv$", GoogleDriveHandle); -// search the cache for file -self.OS.API.onsearch("Google Drive", function(t) { - const arr = []; - const term = new RegExp(t, "i"); - for (let k in G_CACHE) { - const v = G_CACHE[k]; - if ((k.match(term)) || (v && v.mime.match(term))) { - const file = k.asFileHandle(); - file.text = file.basename; - file.mime = v.mime; - file.iconclass = "fa fa-file"; - if (file.mime === "dir") { file.iconclass = "fa fa-folder"; } - file.complex = true; - file.detail = [{ text: file.path }]; - arr.push(file); - } - } - return arr; -}); - -self.OS.onexit("cleanUpGoogleDrive", function() { - G_CACHE = { "gdv://": { id: "root", mime: 'dir' } }; - if (!Ant.OS.setting.VFS.gdrive || !Ant.OS.API.libready(Ant.OS.setting.VFS.gdrive.apilink)) { return; } - const auth2 = gapi.auth2.getAuthInstance(); - if (!auth2) { return; } - if (auth2.isSignedIn.get()) { - let el; - return el = $('