mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
feature (plg_application_office): libreoffice lowa
This commit is contained in:
parent
083eac248f
commit
8a36ba943a
7 changed files with 573 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_application_office"
|
||||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd"
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_htpasswd"
|
||||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_ldap"
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_ldap"
|
||||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_local"
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_authenticate_local"
|
||||||
|
|
|
||||||
18
server/plugin/plg_application_office/Makefile
Normal file
18
server/plugin/plg_application_office/Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
all:
|
||||||
|
make build
|
||||||
|
make install
|
||||||
|
|
||||||
|
install:
|
||||||
|
zip -r application_office.zip . -x "lib/vendor/*"
|
||||||
|
mv application_office.zip ../../../dist/data/state/plugins/
|
||||||
|
|
||||||
|
build:
|
||||||
|
make deps_zeta
|
||||||
|
|
||||||
|
deps_zeta:
|
||||||
|
[ -d lib/lowa ] || mkdir lib/lowa
|
||||||
|
curl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.js > lib/lowa/soffice.js
|
||||||
|
curl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.wasm > lib/lowa/soffice.wasm.br
|
||||||
|
curl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.data.js.metadata > lib/lowa/soffice.data.js.metadata
|
||||||
|
curl https://cdn.zetaoffice.net/zetaoffice_latest/soffice.data > lib/lowa/soffice.data.br
|
||||||
|
curl https://zetaoffice.net/demos/standalone/assets/vendor/zetajs/zeta.js > lib/lowa/zeta.js
|
||||||
61
server/plugin/plg_application_office/loader_lowa.css
Normal file
61
server/plugin/plg_application_office/loader_lowa.css
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/fontawesome.min.css");
|
||||||
|
|
||||||
|
.component_word {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
.component_word canvas {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.component_menubar .action-item .texteditor svg.component_icon {
|
||||||
|
width: 16px;
|
||||||
|
margin: 1px 0 0 2px;
|
||||||
|
}
|
||||||
|
.component_menubar .action-item .texteditor.active svg.component_icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component_menubar .action-item .texteditor input[type="number"] {
|
||||||
|
width: 15px;
|
||||||
|
color: var(--color);
|
||||||
|
border: 2px solid #f2f2f2;
|
||||||
|
background: #f2f2f2;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 3px 0 3px;
|
||||||
|
margin: 0 3px;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
.component_menubar .action-item .texteditor input[type="number"]::-webkit-outer-spin-button,
|
||||||
|
.component_menubar .action-item .texteditor input[type="number"]::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component_menubar .action-item .texteditor .fontawesome {
|
||||||
|
font-family: "FontAwesome";
|
||||||
|
color: var(--light);
|
||||||
|
background: inherit;
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.component_menubar .action-item .texteditor select.fontawesome option {
|
||||||
|
background: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.component_menubar .action-item .texteditor.picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.component_menubar .action-item .texteditor.picker input[type="color"] {
|
||||||
|
padding: 0;
|
||||||
|
width: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.component_word + .component_loader {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
margin-top: 150px;
|
||||||
|
}
|
||||||
18
server/plugin/plg_application_office/loader_lowa.go
Normal file
18
server/plugin/plg_application_office/loader_lowa.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package plg_application_office
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Hooks.Register.Middleware(func(h HandlerFunc) HandlerFunc {
|
||||||
|
return func(app *App, res http.ResponseWriter, req *http.Request) {
|
||||||
|
head := res.Header()
|
||||||
|
head.Set("Cross-Origin-Opener-Policy", "same-origin")
|
||||||
|
head.Set("Cross-Origin-Embedder-Policy", "require-corp")
|
||||||
|
h(app, res, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
286
server/plugin/plg_application_office/loader_lowa.js
Normal file
286
server/plugin/plg_application_office/loader_lowa.js
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
import { createElement, onDestroy } from "../../lib/skeleton/index.js";
|
||||||
|
import rxjs, { effect } from "../../lib/rx.js";
|
||||||
|
import ajax from "../../lib/ajax.js";
|
||||||
|
import { qs } from "../../lib/dom.js";
|
||||||
|
import { join } from "../../lib/path.js";
|
||||||
|
import { loadJS, loadCSS } from "../../helpers/loader.js";
|
||||||
|
import { buttonDownload } from "../../pages/viewerpage/component_menubar.js";
|
||||||
|
import { $ICON } from "../../pages/viewerpage/common_fab.js";
|
||||||
|
import { save } from "../../pages/viewerpage/model_files.js";
|
||||||
|
import "../../components/fab.js";
|
||||||
|
|
||||||
|
import { $toolbar } from "./lib/dom.js";
|
||||||
|
|
||||||
|
await loadCSS(import.meta.url, "./loader_lowa.css");
|
||||||
|
|
||||||
|
let $canvas = null;
|
||||||
|
window.Module = {
|
||||||
|
uno_scripts: [join(import.meta.url, "./lib/lowa/zeta.js"), join(import.meta.url, "./loader_lowa.uno.js")],
|
||||||
|
locateFile: (path, prefix) => (prefix || join(import.meta.url, "./lib/lowa/")) + path,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function(render, { mime, getDownloadUrl, getFilename, $menubar, acl$ }) {
|
||||||
|
const canWrite = (await acl$.toPromise()).indexOf("POST") >= 0;
|
||||||
|
const $page = createElement(`
|
||||||
|
<div class="component_word">
|
||||||
|
<canvas id="qtcanvas" contenteditable="${canWrite}" style="visibility:hidden"></canvas>
|
||||||
|
<button is="component-fab" class="hidden"></button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
render($page);
|
||||||
|
|
||||||
|
// feature1: init
|
||||||
|
const filename = getFilename();
|
||||||
|
const $fab = qs($page, `[is="component-fab"]`);
|
||||||
|
const $qcanvas = qs($page, "canvas");
|
||||||
|
if ($canvas) {
|
||||||
|
$qcanvas.remove();
|
||||||
|
$page.appendChild($canvas);
|
||||||
|
} else {
|
||||||
|
$canvas = $qcanvas;
|
||||||
|
}
|
||||||
|
Object.assign($canvas.style, {
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
});
|
||||||
|
|
||||||
|
// feature2: toolbar init
|
||||||
|
if (canWrite) {
|
||||||
|
$menubar.add(buttonDownload(filename, getDownloadUrl()));
|
||||||
|
if (isWriter(mime)) {
|
||||||
|
$menubar.add($toolbar.bullet);
|
||||||
|
$menubar.add($toolbar.alignment);
|
||||||
|
$menubar.add($toolbar.title);
|
||||||
|
}
|
||||||
|
$menubar.add($toolbar.size);
|
||||||
|
$menubar.add($toolbar.strike);
|
||||||
|
$menubar.add($toolbar.underline);
|
||||||
|
$menubar.add($toolbar.italic);
|
||||||
|
$menubar.add($toolbar.bold);
|
||||||
|
$menubar.add($toolbar.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// feature3: setup lowa
|
||||||
|
window.Module.canvas = $canvas;
|
||||||
|
await loadJS(import.meta.url, "./lib/lowa/soffice.js");
|
||||||
|
let port = await Module.uno_main;
|
||||||
|
onDestroy(() => {
|
||||||
|
$canvas.style.visibility = "hidden";
|
||||||
|
port.postMessage({ cmd: "destroy", mime });
|
||||||
|
});
|
||||||
|
|
||||||
|
// feature4: display rule for save button
|
||||||
|
const action$ = new rxjs.Subject();
|
||||||
|
if (canWrite) effect(rxjs.merge(rxjs.fromEvent($canvas, "keyup"), action$).pipe(rxjs.tap(() => {
|
||||||
|
$fab.classList.remove("hidden");
|
||||||
|
$fab.render($ICON.SAVING);
|
||||||
|
$fab.onclick = () => {
|
||||||
|
$fab.render($ICON.LOADING);
|
||||||
|
$fab.disabled = true;
|
||||||
|
port.postMessage({ cmd: "save" });
|
||||||
|
};
|
||||||
|
})));
|
||||||
|
|
||||||
|
// feature5: load file
|
||||||
|
await effect(ajax({ url: getDownloadUrl(), responseType: "arraybuffer" }).pipe(
|
||||||
|
rxjs.mergeMap(async ({ response }) => {
|
||||||
|
try { FS.mkdir("/tmp/office/"); } catch {}
|
||||||
|
await FS.writeFile("/tmp/office/" + filename , new Uint8Array(response));
|
||||||
|
await port.postMessage({ cmd: "load", filename, mime });
|
||||||
|
onDestroy(() => FS.unlink("/tmp/office/" + filename));
|
||||||
|
$canvas.focus();
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
port.onmessage = function(e) {
|
||||||
|
switch (e.data.cmd) {
|
||||||
|
case "loaded":
|
||||||
|
window.dispatchEvent(new Event("resize"));
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
$canvas.style.visibility = "visible";
|
||||||
|
}, 250);
|
||||||
|
break;
|
||||||
|
case "save":
|
||||||
|
const bytes = FS.readFile("/tmp/office/" + filename);
|
||||||
|
effect(save(new Blob([bytes], {})).pipe(rxjs.tap(() => {
|
||||||
|
$fab.classList.add("hidden");
|
||||||
|
$fab.render($ICON.SAVING);
|
||||||
|
$fab.disabled = false;
|
||||||
|
})));
|
||||||
|
break;
|
||||||
|
case "setFormat":
|
||||||
|
switch(e.data.id) {
|
||||||
|
case "Bold":
|
||||||
|
e.data.state ? $toolbar.bold.classList.add("active") : $toolbar.bold.classList.remove("active");
|
||||||
|
break;
|
||||||
|
case "Italic":
|
||||||
|
e.data.state ? $toolbar.italic.classList.add("active") : $toolbar.italic.classList.remove("active");
|
||||||
|
break;
|
||||||
|
case "Underline":
|
||||||
|
e.data.state ? $toolbar.underline.classList.add("active") : $toolbar.underline.classList.remove("active");
|
||||||
|
break;
|
||||||
|
case "Strikeout":
|
||||||
|
e.data.state ? $toolbar.strike.classList.add("active") : $toolbar.strike.classList.remove("active");
|
||||||
|
break;
|
||||||
|
case "LeftPara":
|
||||||
|
if (e.data.state) qs($toolbar.alignment, "select").value = "left";
|
||||||
|
break;
|
||||||
|
case "RightPara":
|
||||||
|
if (e.data.state) qs($toolbar.alignment, "select").value = "right";
|
||||||
|
break;
|
||||||
|
case "CenterPara":
|
||||||
|
if (e.data.state) qs($toolbar.alignment, "select").value = "center";
|
||||||
|
break;
|
||||||
|
case "JustifyPara":
|
||||||
|
if (e.data.state) qs($toolbar.alignment, "select").value = "justify";
|
||||||
|
break;
|
||||||
|
case "DefaultBullet":
|
||||||
|
qs($toolbar.bullet, "select").value = e.data.state ? "ul" : "normal";
|
||||||
|
break;
|
||||||
|
case "DefaultNumbering":
|
||||||
|
qs($toolbar.bullet, "select").value = e.data.state ? "ol" : "normal";
|
||||||
|
break;
|
||||||
|
case "StyleApply":
|
||||||
|
let value = "normal";
|
||||||
|
if (e.data.state === "Title") value = "title";
|
||||||
|
else if (e.data.state === "Heading 1") value = "head1";
|
||||||
|
else if (e.data.state === "Heading 2") value = "head2";
|
||||||
|
else if (e.data.state === "Heading 3") value = "head3";
|
||||||
|
qs($toolbar.title, "select").value = value;
|
||||||
|
break;
|
||||||
|
case "Color":
|
||||||
|
const hex = e.data.state && e.data.state > 0 ? "#" + e.data.state.toString(16).padStart(6, "0") : "#000000";
|
||||||
|
$toolbar.color.children[0].style.fill = hex;
|
||||||
|
$toolbar.color.children[1].value = hex;
|
||||||
|
break;
|
||||||
|
case "FontHeight":
|
||||||
|
const fontSize = e.data.state;
|
||||||
|
qs($toolbar.size, "input").value = fontSize;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("format", e);
|
||||||
|
throw new Error("Unknown format");
|
||||||
|
}
|
||||||
|
$canvas.focus();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("message", e);
|
||||||
|
throw new Error("Unknown message");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// feature6: toolbar events
|
||||||
|
$toolbar.bold.onclick = () => {
|
||||||
|
$toolbar.bold.classList.toggle("active");
|
||||||
|
action$.next();
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "Bold" });
|
||||||
|
};
|
||||||
|
$toolbar.italic.onclick = () => {
|
||||||
|
$toolbar.italic.classList.toggle("active");
|
||||||
|
action$.next();
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "Italic" });
|
||||||
|
};
|
||||||
|
$toolbar.underline.onclick = () => {
|
||||||
|
$toolbar.underline.classList.toggle("active");
|
||||||
|
action$.next();
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "Underline" });
|
||||||
|
};
|
||||||
|
$toolbar.bullet.onchange = (e) => {
|
||||||
|
switch(e.target.value) {
|
||||||
|
case "normal":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "RemoveBullets" });
|
||||||
|
break;
|
||||||
|
case "ul":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "DefaultBullet" });
|
||||||
|
break;
|
||||||
|
case "ol":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "DefaultNumbering" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
action$.next();
|
||||||
|
};
|
||||||
|
$toolbar.strike.onclick = () => {
|
||||||
|
$toolbar.strike.classList.toggle("active");
|
||||||
|
action$.next();
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "Strikeout" });
|
||||||
|
};
|
||||||
|
$toolbar.alignment.onchange = (e) => {
|
||||||
|
switch(e.target.value) {
|
||||||
|
case "left":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "LeftPara" });
|
||||||
|
break;
|
||||||
|
case "right":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "RightPara" });
|
||||||
|
break;
|
||||||
|
case "center":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "CenterPara" });
|
||||||
|
break;
|
||||||
|
case "justify":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "JustifyPara" });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown tool alignment");
|
||||||
|
}
|
||||||
|
action$.next();
|
||||||
|
};
|
||||||
|
$toolbar.title.onchange = (e) => {
|
||||||
|
switch(e.target.value) {
|
||||||
|
case "normal":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "StyleApply?Style:string=Standard&FamilyName:string=ParagraphStyles" });
|
||||||
|
break;
|
||||||
|
case "title":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "StyleApply?Style:string=Title&FamilyName:string=ParagraphStyles" });
|
||||||
|
break;
|
||||||
|
case "head1":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "StyleApply?Style:string=Heading 1&FamilyName:string=ParagraphStyles" });
|
||||||
|
break;
|
||||||
|
case "head2":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "StyleApply?Style:string=Heading 2&FamilyName:string=ParagraphStyles" });
|
||||||
|
break;
|
||||||
|
case "head3":
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: "StyleApply?Style:string=Heading 3&FamilyName:string=ParagraphStyles" });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown text style");
|
||||||
|
}
|
||||||
|
action$.next();
|
||||||
|
};
|
||||||
|
$toolbar.color.onclick = (e) => {
|
||||||
|
if (e.target.tagName === "INPUT") return;
|
||||||
|
const $svg = e.target.closest("svg")
|
||||||
|
const $input = $svg.nextElementSibling;
|
||||||
|
$input.onchange = (e) => {
|
||||||
|
$svg.style.fill = e.target.value;
|
||||||
|
const color = parseInt(e.target.value.slice(1), 16);
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: `Color?Color:long=${color}` })
|
||||||
|
};
|
||||||
|
$input.click();
|
||||||
|
action$.next();
|
||||||
|
};
|
||||||
|
effect(rxjs.fromEvent(qs($toolbar.size, "input"), "keyup").pipe(
|
||||||
|
rxjs.debounceTime(250),
|
||||||
|
rxjs.tap((e) => {
|
||||||
|
const fontSize = parseInt(e.target.value);
|
||||||
|
port.postMessage({ cmd: "toggleFormatting", id: `FontHeight?FontHeight.Height:float=${fontSize}` });
|
||||||
|
action$.next();
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
|
// feature7: workaround known lowa bug
|
||||||
|
// - when pressing escape, lowa goes out of fullscreen and show some unwanted stuff
|
||||||
|
// - context menu functions like "replace" image which does crash everything with errors generated from soffice.js
|
||||||
|
// - ctrl + s is broken
|
||||||
|
effect(rxjs.fromEvent($page, "keydown", { capture: true }).pipe(rxjs.tap((e) => {
|
||||||
|
if (e.key === "Escape") e.stopPropagation();
|
||||||
|
if (e.key === "s" && e.ctrlKey) e.stopPropagation();
|
||||||
|
})));
|
||||||
|
effect(rxjs.fromEvent($page, "mousedown", { capture: true }).pipe(rxjs.tap((e) => {
|
||||||
|
if (e.which === 3) e.stopPropagation();
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWriter(mime) {
|
||||||
|
return ["application/word", "application/msword", "application/rtf", "application/vnd.oasis.opendocument.text"].indexOf(mime) >= 0;
|
||||||
|
}
|
||||||
123
server/plugin/plg_application_office/loader_lowa.uno.js
Normal file
123
server/plugin/plg_application_office/loader_lowa.uno.js
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
// reference:
|
||||||
|
// - uno programming: https://www.youtube.com/watch?v=CzxLKG9CUvo
|
||||||
|
// - dispatch commands: https://wiki.documentfoundation.org/Development/DispatchCommands
|
||||||
|
Module.zetajs.then(function(zetajs) {
|
||||||
|
init({
|
||||||
|
css: zetajs.uno.com.sun.star,
|
||||||
|
zetajs,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function init({ zetajs, css }) {
|
||||||
|
const context = zetajs.getUnoComponentContext();
|
||||||
|
const desktop = css.frame.Desktop.create(context);
|
||||||
|
let ctrl, xModel;
|
||||||
|
|
||||||
|
// UI Element: remove toolbar in writer
|
||||||
|
const config = css.configuration.ReadWriteAccess.create(context, "en-US");
|
||||||
|
["Writer", "Calc", "Impress"].forEach((app) => {
|
||||||
|
const uielems = config.getByHierarchicalName(`/org.openoffice.Office.UI.${app}WindowState/UIElements/States`);
|
||||||
|
for (const i of uielems.getElementNames()) {
|
||||||
|
const uielem = uielems.getByName(i);
|
||||||
|
if (uielem.getByName("Visible")) uielem.setPropertyValue("Visible", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Theme & Colors
|
||||||
|
const elmnts = config.getByHierarchicalName("/org.openoffice.Office.UI/ColorScheme/ColorSchemes");
|
||||||
|
for (const i of elmnts.getElementNames()) {
|
||||||
|
const colorScheme = elmnts.getByName(i);
|
||||||
|
// console.log(colorScheme.getElementNames());
|
||||||
|
colorScheme.getByName("AppBackground").setPropertyValue("Color", 16119285); // #f5f5f5
|
||||||
|
colorScheme.getByName("WriterPageBreaks").setPropertyValue("Color", 16119285); // #f5f5f5
|
||||||
|
colorScheme.getByName("WriterSectionBoundaries").setPropertyValue("Color", 16119285); // #f5f5f5
|
||||||
|
colorScheme.getByName("Shadow").setPropertyValue("Color", 16119285); // #f5f5f5
|
||||||
|
colorScheme.getByName("FontColor").setPropertyValue("Color", 2368548); // #242424
|
||||||
|
colorScheme.getByName("WriterHeaderFooterMark").setPropertyValue("Color", 16777215); // #ffffff
|
||||||
|
}
|
||||||
|
config.commitChanges();
|
||||||
|
|
||||||
|
zetajs.mainPort.onmessage = function(e) {
|
||||||
|
switch (e.data.cmd) {
|
||||||
|
case "destroy":
|
||||||
|
toggleTools({ mime: e.data.mime, css, ctrl, context });
|
||||||
|
xModel = null;
|
||||||
|
ctrl = null;
|
||||||
|
break;
|
||||||
|
case "load":
|
||||||
|
const { filename, mime } = e.data;
|
||||||
|
const in_path = `file:///tmp/office/${filename}`;
|
||||||
|
xModel = desktop.loadComponentFromURL(in_path, "_default", 0, []);
|
||||||
|
ctrl = xModel.getCurrentController();
|
||||||
|
ctrl.getFrame().LayoutManager.hideElement("private:resource/menubar/menubar");
|
||||||
|
ctrl.getFrame().LayoutManager.hideElement("private:resource/statusbar/statusbar");
|
||||||
|
ctrl.getFrame().getContainerWindow().FullScreen = true;
|
||||||
|
toggleTools({ mime, css, ctrl, context });
|
||||||
|
const commands = [ // ref: https://wiki.documentfoundation.org/Development/DispatchCommands
|
||||||
|
"Bold", "Italic", "Underline", "Strikeout", "LeftPara", "RightPara", "CenterPara",
|
||||||
|
"JustifyPara", "Color", "FontHeight", ...(isWriter(mime) ? ["StyleApply", "DefaultBullet", "DefaultNumbering"] : []),
|
||||||
|
];
|
||||||
|
for (const id of commands) {
|
||||||
|
const urlObj = transformUrl(".uno:" + id, { css, context });
|
||||||
|
const listener = zetajs.unoObject([css.frame.XStatusListener], {
|
||||||
|
disposing: function(source) {},
|
||||||
|
statusChanged: function(state) {
|
||||||
|
state = zetajs.fromAny(state.State);
|
||||||
|
if (id === "StyleApply") state = state && state.StyleName || null;
|
||||||
|
else if (id === "Color") state = typeof state === "number" ? state : null;
|
||||||
|
else if (id === "FontHeight") state = state && state.Height || null;
|
||||||
|
else if (typeof state !== "boolean") state = false;
|
||||||
|
|
||||||
|
if (state === null) return;
|
||||||
|
zetajs.mainPort.postMessage({ cmd: "setFormat", id, state });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
queryDispatch(urlObj, { ctrl }).addStatusListener(listener, urlObj);
|
||||||
|
}
|
||||||
|
zetajs.mainPort.postMessage({ cmd: "loaded" });
|
||||||
|
break;
|
||||||
|
case "save":
|
||||||
|
xModel.store();
|
||||||
|
zetajs.mainPort.postMessage({ cmd: "save" });
|
||||||
|
break;
|
||||||
|
case "toggleFormatting":
|
||||||
|
dispatch(".uno:" + e.data.id, { css, ctrl, context });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw Error("Unknown message command: " + e.data.cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformUrl(unoUrl, { css, context }) {
|
||||||
|
const ioparam = {
|
||||||
|
val: new css.util.URL({
|
||||||
|
Complete: unoUrl
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
css.util.URLTransformer.create(context).parseStrict(ioparam);
|
||||||
|
return ioparam.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryDispatch(urlObj, { ctrl }) {
|
||||||
|
return ctrl.queryDispatch(urlObj, "_self", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatch(unoUrl, { css, ctrl, context }) {
|
||||||
|
const urlObj = transformUrl(unoUrl, { css, context });
|
||||||
|
queryDispatch(urlObj, { ctrl }).dispatch(urlObj, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTools({ css, ctrl, context, mime }) {
|
||||||
|
dispatch(".uno:Sidebar", { css, ctrl, context });
|
||||||
|
if (isCalc(mime)) dispatch(".uno:InputLineVisible", { css, ctrl, context });
|
||||||
|
if (isWriter(mime)) dispatch(".uno:Ruler", { css, ctrl, context });
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWriter(mime) {
|
||||||
|
return ["application/word", "application/msword", "application/rtf", "application/vnd.oasis.opendocument.text"].indexOf(mime) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCalc(mime) {
|
||||||
|
return ["application/excel", "application/vnd.ms-excel", "application/vnd.oasis.opendocument.spreadsheet"].indexOf(mime) >= 0;
|
||||||
|
}
|
||||||
66
server/plugin/plg_application_office/manifest.json
Normal file
66
server/plugin/plg_application_office/manifest.json
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"author": "Filestash Pty Ltd",
|
||||||
|
"version": "v0.0",
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/word",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/msword",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/rtf",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/vnd.oasis.opendocument.text",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/excel",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/vnd.ms-excel",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/vnd.oasis.opendocument.spreadsheet",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/powerpoint",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/vnd.ms-powerpoint",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "xdg-open",
|
||||||
|
"mime": "application/vnd.oasis.opendocument.presentation",
|
||||||
|
"entrypoint": "loader_lowa.js",
|
||||||
|
"application": "skeleton"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue