feat(oo): native conversion, deep links, sheet export (re-land) #75

Merged
eSlider merged 9 commits from feat/oo-conversion-links into main 2026-09-22 22:42:07 +01:00
5 changed files with 33 additions and 5 deletions
Showing only changes of commit 520d6d05c8 - Show all commits
+4 -2
View File
@@ -103,8 +103,10 @@ func (c *Client) authenticateOnce(ctx context.Context) error {
// token, and the portal error otherwise. // token, and the portal error otherwise.
// //
// OnlyOffice accepts either the userName or the account email as the login. On // OnlyOffice accepts either the userName or the account email as the login. On
// some portals the userName login fails while the email works — use this probe // the arc-1 portal (office.produktor.io) the account userName login returns
// to tell them apart before sharing credentials. // HTTP 500 "User authentication failed" while the account email succeeds —
// confirmed for a freshly created guest user. Use this probe before sharing
// credentials (see `oo users check`), and prefer the email as the login.
func (c *Client) AuthenticateAs(ctx context.Context, login, password string) error { func (c *Client) AuthenticateAs(ctx context.Context, login, password string) error {
body, err := json.Marshal(Credentials{User: login, Password: password}) body, err := json.Marshal(Credentials{User: login, Password: password})
if err != nil { if err != nil {
+5
View File
@@ -9,6 +9,11 @@ func init() {
rootCmd.AddCommand(linkCmd()) rootCmd.AddCommand(linkCmd())
} }
// linkCmd prints deep links for file ids. Used to build the third-party
// document packs (e.g. the arc-1 "Lisbon apartment" Info cover embeds links to
// passports, contracts and the income documents). File ids come from
// `oo projects files list` / `oo dav ls`; `oo projects files replace-in` keeps
// them clean when a document is re-uploaded.
func linkCmd() *cobra.Command { func linkCmd() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "link FILE_ID [FILE_ID...]", Use: "link FILE_ID [FILE_ID...]",
+6 -1
View File
@@ -157,7 +157,12 @@ func prjFilesReplaceInCmd() *cobra.Command {
Short: "Replace same-named file(s) in a folder: hard delete + fresh upload (no version history)", Short: "Replace same-named file(s) in a folder: hard delete + fresh upload (no version history)",
Long: `Deletes any file in FOLDER_ID with the same stem|ext (hard delete — the CLI Long: `Deletes any file in FOLDER_ID with the same stem|ext (hard delete — the CLI
delete is permanent) and uploads the local file fresh. Unlike 'update' this delete is permanent) and uploads the local file fresh. Unlike 'update' this
leaves a single clean version, which matters when the file id is shared. leaves a single clean version.
Why it exists: on the arc-1 portal, repeated 'update' of a shared document
(the apartment "Info" cover and the Edelweiss contract) accumulated a visible
version history and a stale id. replace-in yields one clean revision; then
point links at the returned id (or keep an nginx alias for the legacy fileid).
Note: file ids are server-assigned; a fresh upload gets a new id.`, Note: file ids are server-assigned; a fresh upload gets a new id.`,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
+7 -2
View File
@@ -319,8 +319,13 @@ func usersCheckCmd() *cobra.Command {
Use: "check", Use: "check",
Short: "Check that a login can authenticate (userName or email)", Short: "Check that a login can authenticate (userName or email)",
Long: `Probes POST /api/2.0/authentication.json with the given credentials and Long: `Probes POST /api/2.0/authentication.json with the given credentials and
discards the token. On this portal the account email is the reliable login discards the token.
identifier (userName login may fail); use this before sharing credentials.`,
Where this is used: before handing portal credentials to an external party
(e.g. a landlord given read access to the apartment document pack), verify the
login actually works. On the arc-1 portal the account email is the reliable
login identifier — the userName login fails with 500 for a freshly created
user, so share the email, not the userName.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if login == "" { if login == "" {
return fmt.Errorf("--login is required (userName or email)") return fmt.Errorf("--login is required (userName or email)")
+11
View File
@@ -1,6 +1,17 @@
package onlyoffice package onlyoffice
// Deep links to OnlyOffice portal objects. // Deep links to OnlyOffice portal objects.
//
// Where this is used: third-party-facing document packs (e.g. the arc-1
// office.produktor.io "Lisbon apartment" project) embed per-file links of the
// form /Products/Files/DocEditor.aspx?fileid=<id> in a cover document and in
// chat messages. Those links must be generated consistently so they match the
// file ids returned by `oo projects files list` / `oo link`.
//
// Caveat proven on the arc-1 portal: file ids are server-assigned and a
// re-upload/delete yields a NEW id, so an already-shared link can go stale.
// Use `oo projects files replace-in` (keep the id clean) and, if a legacy link
// must keep working, an nginx alias can 302 the old fileid to the new one.
import ( import (
"fmt" "fmt"