main
py 167 lines 7.43 KB
Raw
1 from __future__ import annotations
2
3 from helpers.api import ApiHandler, Request
4 from plugins._desktop.helpers import desktop_session
5 from plugins._office.helpers import document_store, libreoffice
6
7
8 class OfficeSession(ApiHandler):
9 async def process(self, input: dict, request: Request) -> dict:
10 action = str(input.get("action") or "open").lower().strip()
11 context_id = str(input.get("ctxid") or input.get("context_id") or "").strip()
12
13 if action == "status":
14 return libreoffice.collect_status()
15 if action == "home":
16 return {"ok": True, "path": document_store.default_open_path(context_id)}
17 if action == "close":
18 closed = document_store.close_session(
19 session_id=str(input.get("session_id") or ""),
20 file_id=str(input.get("file_id") or ""),
21 )
22 return {"ok": True, "closed": closed}
23 if action == "create":
24 fmt = str(input.get("format") or "odt").lower().strip().lstrip(".")
25 if fmt not in desktop_session.OFFICIAL_EXTENSIONS:
26 return {"ok": False, "error": f"Office can only create LibreOffice formats, not .{fmt}."}
27 try:
28 doc = document_store.create_document(
29 kind=str(input.get("kind") or "document"),
30 title=str(input.get("title") or "Untitled"),
31 fmt=fmt,
32 content=str(input.get("content") or ""),
33 path=str(input.get("path") or ""),
34 context_id=context_id,
35 )
36 except ValueError as exc:
37 return {"ok": False, "error": str(exc)}
38 if doc["extension"] in {"odt", "ods", "odp"}:
39 validation = libreoffice.validate_odf(doc["path"])
40 if not validation.get("ok"):
41 return {"ok": False, "error": validation.get("error") or "ODF validation failed."}
42 if doc["extension"] == "docx":
43 validation = libreoffice.validate_docx(doc["path"])
44 if not validation.get("ok"):
45 return {"ok": False, "error": validation.get("error") or "DOCX validation failed."}
46 return await self._open_document(doc, input, request)
47 if action == "open":
48 file_id = str(input.get("file_id") or "").strip()
49 try:
50 doc = (
51 document_store.get_document(file_id)
52 if file_id
53 else document_store.register_document(str(input.get("path") or ""), context_id=context_id)
54 )
55 except ValueError as exc:
56 return {"ok": False, "error": str(exc)}
57 return await self._open_document(doc, input, request)
58 if action == "save":
59 return self._save(input)
60 if action == "renamed":
61 return self._renamed(input, context_id)
62 return {"ok": False, "error": f"Unsupported office session action: {action}"}
63
64 async def _open_document(self, doc: dict, input: dict, request: Request) -> dict:
65 mode = "edit" if str(input.get("mode") or "edit").lower() == "edit" else "view"
66 if str(doc.get("extension") or "").lower() in document_store.EDITOR_TEXT_EXTENSIONS:
67 return {
68 "ok": False,
69 "error": "Text documents use the Editor surface.",
70 "document": _public_doc(doc),
71 }
72 if str(doc.get("extension") or "").lower() in desktop_session.OFFICIAL_EXTENSIONS:
73 if input.get("open_in_desktop") is not True:
74 return {
75 "ok": True,
76 "requires_desktop": True,
77 "file_id": doc["file_id"],
78 "title": doc["basename"],
79 "extension": doc["extension"],
80 "path": doc["path"],
81 "text": "",
82 "document": _public_doc(doc),
83 "version": document_store.item_version(doc),
84 "mode": mode,
85 }
86 store_session = document_store.create_session(
87 doc["file_id"],
88 user_id=str(input.get("user_id") or "agent-zero-user"),
89 permission="write" if mode == "edit" else "read",
90 origin=self._origin(request),
91 )
92 desktop = desktop_session.get_manager().open(doc, refresh=input.get("refresh") is True)
93 if not desktop.get("available"):
94 document_store.close_session(session_id=store_session["session_id"])
95 return {
96 "ok": False,
97 "error": desktop.get("error") or desktop.get("reason") or "Desktop session is unavailable.",
98 "desktop": desktop,
99 "libreoffice": libreoffice.collect_status(),
100 }
101 return {
102 "ok": True,
103 "session_id": desktop["session_id"],
104 "desktop_session_id": desktop["session_id"],
105 "file_id": doc["file_id"],
106 "title": doc["basename"],
107 "extension": doc["extension"],
108 "path": doc["path"],
109 "text": "",
110 "document": _public_doc(doc),
111 "version": document_store.item_version(doc),
112 "desktop": desktop,
113 "store_session_id": store_session["session_id"],
114 "mode": mode,
115 }
116 return {"ok": False, "error": f".{doc.get('extension', '')} documents are not supported by LibreOffice."}
117
118 def _save(self, input: dict) -> dict:
119 return {"ok": False, "error": "Text document saves use /plugins/_editor/editor_session."}
120
121 def _renamed(self, input: dict, context_id: str = "") -> dict:
122 file_id = str(input.get("file_id") or "").strip()
123 path = str(input.get("path") or "").strip()
124 if not file_id:
125 return {"ok": False, "error": "file_id is required."}
126 if not path:
127 return {"ok": False, "error": "path is required."}
128 try:
129 updated = document_store.rename_document(
130 file_id,
131 path,
132 content=input.get("text") if "text" in input else None,
133 context_id=context_id,
134 )
135 except Exception as exc:
136 return {"ok": False, "error": str(exc)}
137 desktop = None
138 if str(updated.get("extension") or "").lower() in desktop_session.OFFICIAL_EXTENSIONS:
139 desktop = desktop_session.get_manager().retarget_document(file_id, updated)
140 return {
141 "ok": True,
142 "document": _public_doc(updated),
143 "version": document_store.item_version(updated),
144 "desktop": desktop,
145 "refreshFiles": False,
146 }
147
148 def _origin(self, request: Request) -> str:
149 origin = request.headers.get("Origin") or request.host_url.rstrip("/")
150 return origin.rstrip("/")
151
152
153 def _public_doc(doc: dict) -> dict:
154 result = {
155 "file_id": doc["file_id"],
156 "path": document_store.display_path(doc["path"]),
157 "basename": doc["basename"],
158 "title": doc["basename"],
159 "extension": doc["extension"],
160 "size": doc["size"],
161 "version": document_store.item_version(doc),
162 "last_modified": doc["last_modified"],
163 }
164 for key in ("open_sessions", "last_opened_at", "session_expires_at"):
165 if key in doc:
166 result[key] = doc[key]
167 return result