chore: keep the re-landed feature files showroom-safe
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go stable) (pull_request) Successful in 1m21s
Tests / Test (Go 1.25) (pull_request) Successful in 1m27s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go stable) (pull_request) Successful in 1m21s
Tests / Test (Go 1.25) (pull_request) Successful in 1m27s
The conversion / link / sheet commits predate the tree cleanup, so their comments and README/test examples still carried internal refs. Re-apply the genericization (no internal hosts, personal names or client file ids).
This commit is contained in:
@@ -557,7 +557,7 @@ Example — convert a portal file (or a local file) to PDF with the native engin
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
export ONLYOFFICE_DS_SECRET=<DocumentServer CoAuthoring secret>
|
export ONLYOFFICE_DS_SECRET=<DocumentServer CoAuthoring secret>
|
||||||
oo docs pdf 3684 --out out.pdf # OO file id → PDF
|
oo docs pdf 1234 --out out.pdf # OO file id → PDF
|
||||||
oo docs pdf ./report.docx --to pdf # local file → PDF (temp upload, auto-cleanup)
|
oo docs pdf ./report.docx --to pdf # local file → PDF (temp upload, auto-cleanup)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -619,35 +619,35 @@ oo cases list
|
|||||||
oo crm-tasks categories
|
oo crm-tasks categories
|
||||||
|
|
||||||
# Deep links & native document conversion
|
# Deep links & native document conversion
|
||||||
oo link 3684 3694 # DocEditor URL for file ids (title + url)
|
oo link 1234 2345 # DocEditor URL for file ids (title + url)
|
||||||
oo docs presigned 3684 # short-lived fetchable URL of an OO file
|
oo docs presigned 1234 # short-lived fetchable URL of an OO file
|
||||||
oo docs pdf 3684 --out out.pdf # OO file → PDF (via DocumentServer)
|
oo docs pdf 1234 --out out.pdf # OO file → PDF (via DocumentServer)
|
||||||
oo docs pdf ./report.docx --to pdf # local file → PDF on the fly (temp upload+cleanup)
|
oo docs pdf ./report.docx --to pdf # local file → PDF on the fly (temp upload+cleanup)
|
||||||
oo docs pdf 3684 --stream > out.pdf # pipe: bytes to stdout (alias --pipe)
|
oo docs pdf 1234 --stream > out.pdf # pipe: bytes to stdout (alias --pipe)
|
||||||
|
|
||||||
# Spreadsheet export (sheet-aware; local reader — the DS csv output is first-sheet-only)
|
# Spreadsheet export (sheet-aware; local reader — the DS csv output is first-sheet-only)
|
||||||
oo docs csv 227 --sheet 2 # XLS/XLSX/ODS worksheet → CSV (--sheet N, 1-based)
|
oo docs csv 1234 --sheet 2 # XLS/XLSX/ODS worksheet → CSV (--sheet N, 1-based)
|
||||||
oo docs csv 227 --delimiter ';' # ; | | \t via --delimiter
|
oo docs csv 1234 --delimiter ';' # ; | | \t via --delimiter
|
||||||
oo docs json 227 --sheet 2 # worksheet → JSON rows (first row = header)
|
oo docs json 1234 --sheet 2 # worksheet → JSON rows (first row = header)
|
||||||
oo docs csv ./book.xlsx --sheet 1 --out sheet1.csv
|
oo docs csv ./book.xlsx --sheet 1 --out sheet1.csv
|
||||||
# export ONLYOFFICE_DS_SECRET=<DocumentServer CoAuthoring secret>
|
# export ONLYOFFICE_DS_SECRET=<DocumentServer CoAuthoring secret>
|
||||||
# docs base: $ONLYOFFICE_DOCS_URL, else $ONLYOFFICE_URL + /ds-vpath
|
# docs base: $ONLYOFFICE_DOCS_URL, else $ONLYOFFICE_URL + /ds-vpath
|
||||||
|
|
||||||
# Project team (portal users) CRUD
|
# Project team (portal users) CRUD
|
||||||
oo projects team list 219
|
oo projects team list 42
|
||||||
oo projects team add 219 <user_id> [<user_id>...]
|
oo projects team add 42 <user_id> [<user_id>...]
|
||||||
oo projects team remove 219 <user_id>
|
oo projects team remove 42 <user_id>
|
||||||
oo projects team set 219 <user_id> [...] # replace team (may lag; verify with list)
|
oo projects team set 42 <user_id> [...] # replace team (may lag; verify with list)
|
||||||
oo projects milestone-delete 38
|
oo projects milestone-delete 7
|
||||||
|
|
||||||
# Project documents: fresh re-upload (single clean version) / new version
|
# Project documents: fresh re-upload (single clean version) / new version
|
||||||
oo projects files replace-in 495 ./contract.pdf # hard delete same stem|ext in folder + upload
|
oo projects files replace-in 1 ./contract.pdf # hard delete same stem|ext in folder + upload
|
||||||
oo projects files update 3647 ./contract.docx # overwrite content, same file id
|
oo projects files update 1 ./contract.docx # overwrite content, same file id
|
||||||
|
|
||||||
# Users lifecycle
|
# Users lifecycle
|
||||||
oo users list ; oo users get <user_id>
|
oo users list ; oo users get <user_id>
|
||||||
oo users create --first Morgane --last Avéus --email morgane@example.com --password '…'
|
oo users create --first Jane --last Doe --email jane.doe@example.com --password '…'
|
||||||
oo users check --login morgane@example.com # verify login (email works when userName 500s)
|
oo users check --login jane.doe@example.com # verify login (email works when userName 500s)
|
||||||
oo users update <user_id> --title "…" --location "…"
|
oo users update <user_id> --title "…" --location "…"
|
||||||
oo users block <user_id> ; oo users unblock <user_id>
|
oo users block <user_id> ; oo users unblock <user_id>
|
||||||
oo users password <user_id> # reads the new password from stdin
|
oo users password <user_id> # reads the new password from stdin
|
||||||
@@ -835,7 +835,7 @@ kontolink IN.xlsx oo-index.tsv OUT.xlsx [FILE_ID] [AMOUNTS_TSV]
|
|||||||
| `mails` | `accounts`, `folders`, `list`, `get`, `download-attachment`, `draft`, `attach`, `draft-invoice`, `send`, `delete` |
|
| `mails` | `accounts`, `folders`, `list`, `get`, `download-attachment`, `draft`, `attach`, `draft-invoice`, `send`, `delete` |
|
||||||
| `cases` | `list`, `create`, `delete`, `member-add` |
|
| `cases` | `list`, `create`, `delete`, `member-add` |
|
||||||
| `crm-tasks` | `list`, `create`, `delete`, `categories`, `reassign-self` |
|
| `crm-tasks` | `list`, `create`, `delete`, `categories`, `reassign-self` |
|
||||||
| `docs` | `tools`, `convert`, `optimize`, `ocr`, `hocr`, `as-md`, `put-md`, `put-txt`, `put-xlsx` |
|
| `docs` | `tools`, `convert`, `pdf`, `presigned`, `csv`, `json`, `optimize`, `ocr`, `hocr`, `as-md`, `put-md`, `put-txt`, `put-xlsx` |
|
||||||
| `catalog` | `match`, `merge`, `apply`, `scan-contacts`, `scan-projects`, `scan-thunderbird` |
|
| `catalog` | `match`, `merge`, `apply`, `scan-contacts`, `scan-projects`, `scan-thunderbird` |
|
||||||
| `dav` | `ls`, `move`, `copy`, `mkdir`, `rename-file`, `rename-folder`, `download`, `fileops` |
|
| `dav` | `ls`, `move`, `copy`, `mkdir`, `rename-file`, `rename-folder`, `download`, `fileops` |
|
||||||
| `search` | `QUERY` (`--content`, `--folder ID`, `--limit N`, `--backend oo\|own`, `--json`) |
|
| `search` | `QUERY` (`--content`, `--folder ID`, `--limit N`, `--backend oo\|own`, `--json`) |
|
||||||
@@ -847,6 +847,12 @@ CLI-only concern — the library itself never loads dotfiles).
|
|||||||
Canonical `ONLYOFFICE_*` variables win over aliases. Optional CLI-only aliases:
|
Canonical `ONLYOFFICE_*` variables win over aliases. Optional CLI-only aliases:
|
||||||
`OO_URL` / `OO_USER` / `OO_PASS` → `ONLYOFFICE_URL` / `ONLYOFFICE_USER` / `ONLYOFFICE_PASS`.
|
`OO_URL` / `OO_USER` / `OO_PASS` → `ONLYOFFICE_URL` / `ONLYOFFICE_USER` / `ONLYOFFICE_PASS`.
|
||||||
|
|
||||||
|
Catalog scanning (`oo catalog scan-projects` / `scan-thunderbird`) classifies
|
||||||
|
clients from rules in `$OO_CATALOG_CONFIG` (or `--config`); see
|
||||||
|
[`catalog/classify.example.yaml`](catalog/classify.example.yaml). Without rules
|
||||||
|
nothing is classified as work. The MinIO download fallback is off unless
|
||||||
|
`MINIO_ENDPOINT` + `MINIO_ACCESS_KEY` + `MINIO_SECRET_KEY` are set.
|
||||||
|
|
||||||
Run `oo --help` or `oo <subject> --help` for the full command reference.
|
Run `oo --help` or `oo <subject> --help` for the full command reference.
|
||||||
|
|
||||||
> **0.5.0 migration note:** the command tree was flattened per-subject. Old
|
> **0.5.0 migration note:** the command tree was flattened per-subject. Old
|
||||||
|
|||||||
@@ -103,10 +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
|
||||||
// the arc-1 portal (office.produktor.io) the account userName login returns
|
// some portals the account userName login returns HTTP 500 "User authentication
|
||||||
// HTTP 500 "User authentication failed" while the account email succeeds —
|
// failed" while the account email succeeds — confirmed for a freshly created
|
||||||
// confirmed for a freshly created guest user. Use this probe before sharing
|
// guest user. Use this probe before sharing credentials (see `oo users check`),
|
||||||
// credentials (see `oo users check`), and prefer the email as the login.
|
// 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 {
|
||||||
|
|||||||
+2
-2
@@ -87,7 +87,7 @@ uploaded to the scratch folder (--folder, default 2 = "My documents"), converted
|
|||||||
downloaded and then removed — so any local document yields a PDF on the fly.
|
downloaded and then removed — so any local document yields a PDF on the fly.
|
||||||
|
|
||||||
Docs base defaults to $ONLYOFFICE_DOCS_URL, else $ONLYOFFICE_URL + "/ds-vpath"
|
Docs base defaults to $ONLYOFFICE_DOCS_URL, else $ONLYOFFICE_URL + "/ds-vpath"
|
||||||
(the portal nginx proxies /ds-vpath to the DocumentServer). The JWT secret is
|
(/ds-vpath is the usual reverse-proxy mount for the DocumentServer). The JWT secret is
|
||||||
$ONLYOFFICE_DS_SECRET (DocumentServer services.CoAuthoring.secret).`,
|
$ONLYOFFICE_DS_SECRET (DocumentServer services.CoAuthoring.secret).`,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
@@ -334,7 +334,7 @@ Each data row becomes an object keyed by the header cells of that sheet.`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// docsBaseURL resolves the DocumentServer base: --docs-url, $ONLYOFFICE_DOCS_URL,
|
// docsBaseURL resolves the DocumentServer base: --docs-url, $ONLYOFFICE_DOCS_URL,
|
||||||
// else the portal's /ds-vpath proxy.
|
// else the standard /ds-vpath reverse-proxy mount.
|
||||||
func docsBaseURL(flag string) string {
|
func docsBaseURL(flag string) string {
|
||||||
if flag != "" {
|
if flag != "" {
|
||||||
return flag
|
return flag
|
||||||
|
|||||||
+4
-5
@@ -9,11 +9,10 @@ func init() {
|
|||||||
rootCmd.AddCommand(linkCmd())
|
rootCmd.AddCommand(linkCmd())
|
||||||
}
|
}
|
||||||
|
|
||||||
// linkCmd prints deep links for file ids. Used to build the third-party
|
// linkCmd prints deep links for file ids. Used to build third-party document
|
||||||
// document packs (e.g. the arc-1 "Lisbon apartment" Info cover embeds links to
|
// packs whose cover embeds links to contracts and supporting files. File ids
|
||||||
// passports, contracts and the income documents). File ids come from
|
// come from `oo projects files list` / `oo dav ls`; `oo projects files
|
||||||
// `oo projects files list` / `oo dav ls`; `oo projects files replace-in` keeps
|
// replace-in` keeps them clean when a document is re-uploaded.
|
||||||
// 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...]",
|
||||||
|
|||||||
@@ -159,9 +159,8 @@ func prjFilesReplaceInCmd() *cobra.Command {
|
|||||||
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.
|
leaves a single clean version.
|
||||||
|
|
||||||
Why it exists: on the arc-1 portal, repeated 'update' of a shared document
|
Why it exists: repeated 'update' of a shared document accumulated a visible
|
||||||
(the apartment "Info" cover and the Edelweiss contract) accumulated a visible
|
version history and left a stale id. replace-in yields one clean revision; then
|
||||||
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).
|
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.`,
|
||||||
|
|||||||
+4
-4
@@ -322,10 +322,10 @@ func usersCheckCmd() *cobra.Command {
|
|||||||
discards the token.
|
discards the token.
|
||||||
|
|
||||||
Where this is used: before handing portal credentials to an external party
|
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
|
(e.g. a guest given read access to a document pack), verify the login actually
|
||||||
login actually works. On the arc-1 portal the account email is the reliable
|
works. On some portals the account email is the reliable login identifier — the
|
||||||
login identifier — the userName login fails with 500 for a freshly created
|
userName login fails with 500 for a freshly created user — so share the email,
|
||||||
user, so share the email, not the userName.`,
|
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)")
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ package onlyoffice
|
|||||||
//
|
//
|
||||||
// The DocumentServer (the same engine behind the portal's "Download as PDF")
|
// The DocumentServer (the same engine behind the portal's "Download as PDF")
|
||||||
// converts any office format. From a portal-reachable host the converter is
|
// converts any office format. From a portal-reachable host the converter is
|
||||||
// exposed at "<portal>/ds-vpath/converter" (nginx proxy) or directly at
|
// exposed at "<portal>/ds-vpath/converter" (reverse proxy) or directly at
|
||||||
// "http://<docs-server>:8083/converter" (legacy path: /ConvertService.ashx).
|
// "http://<docs-server>:8083/converter" (legacy path: /ConvertService.ashx).
|
||||||
//
|
//
|
||||||
// Flow: PresignedURI(fileId) → Convert(docsBase, secret, req) → download
|
// Flow: PresignedURI(fileId) → Convert(docsBase, secret, req) → download
|
||||||
|
|||||||
@@ -2,16 +2,15 @@ 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
|
// Used by tools that embed per-file links of the form
|
||||||
// office.produktor.io "Lisbon apartment" project) embed per-file links of the
|
// /Products/Files/DocEditor.aspx?fileid=<id> in a cover document or a chat
|
||||||
// form /Products/Files/DocEditor.aspx?fileid=<id> in a cover document and in
|
// message. Those links must be consistent so they match the file ids returned
|
||||||
// chat messages. Those links must be generated consistently so they match the
|
// by `oo projects files list` / `oo link`.
|
||||||
// file ids returned by `oo projects files list` / `oo link`.
|
|
||||||
//
|
//
|
||||||
// Caveat proven on the arc-1 portal: file ids are server-assigned and a
|
// Caveat: file ids are server-assigned and a re-upload/delete yields a NEW id,
|
||||||
// re-upload/delete yields a NEW id, so an already-shared link can go stale.
|
// so an already-shared link can go stale. Use `oo projects files replace-in`
|
||||||
// Use `oo projects files replace-in` (keep the id clean) and, if a legacy link
|
// (keeps the id stable) and, if a legacy link must keep working, an nginx alias
|
||||||
// must keep working, an nginx alias can 302 the old fileid to the new one.
|
// can 302 the old fileid to the new one.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import "testing"
|
|||||||
func TestFileEditorURL(t *testing.T) {
|
func TestFileEditorURL(t *testing.T) {
|
||||||
cases := []struct{ base, id, want string }{
|
cases := []struct{ base, id, want string }{
|
||||||
{"https://office.example.com", "3651", "https://office.example.com/Products/Files/DocEditor.aspx?fileid=3651"},
|
{"https://office.example.com", "3651", "https://office.example.com/Products/Files/DocEditor.aspx?fileid=3651"},
|
||||||
{"https://office.example.com/", " 3687 ", "https://office.example.com/Products/Files/DocEditor.aspx?fileid=3687"},
|
{"https://office.example.com/", " 1234 ", "https://office.example.com/Products/Files/DocEditor.aspx?fileid=1234"},
|
||||||
{"http://localhost:8087", "a/b", "http://localhost:8087/Products/Files/DocEditor.aspx?fileid=a%2Fb"},
|
{"http://localhost:8087", "a/b", "http://localhost:8087/Products/Files/DocEditor.aspx?fileid=a%2Fb"},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
|||||||
+3
-3
@@ -17,7 +17,7 @@ func TestWorkbookSheetExport(t *testing.T) {
|
|||||||
_ = f.SetCellValue("first", "B2", 30)
|
_ = f.SetCellValue("first", "B2", 30)
|
||||||
_, _ = f.NewSheet("second")
|
_, _ = f.NewSheet("second")
|
||||||
_ = f.SetCellValue("second", "A1", "city")
|
_ = f.SetCellValue("second", "A1", "city")
|
||||||
_ = f.SetCellValue("second", "A2", "Lisbon")
|
_ = f.SetCellValue("second", "A2", "Berlin")
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := f.Write(&buf); err != nil {
|
if err := f.Write(&buf); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -34,7 +34,7 @@ func TestWorkbookSheetExport(t *testing.T) {
|
|||||||
t.Fatalf("sheet0 csv=%q err=%v", csv0, err)
|
t.Fatalf("sheet0 csv=%q err=%v", csv0, err)
|
||||||
}
|
}
|
||||||
csv1, err := WorkbookSheetCSV(data, 1, 0)
|
csv1, err := WorkbookSheetCSV(data, 1, 0)
|
||||||
if err != nil || !strings.Contains(csv1, "Lisbon") || strings.Contains(csv1, "Alice") {
|
if err != nil || !strings.Contains(csv1, "Berlin") || strings.Contains(csv1, "Alice") {
|
||||||
t.Fatalf("sheet1 csv=%q err=%v", csv1, err)
|
t.Fatalf("sheet1 csv=%q err=%v", csv1, err)
|
||||||
}
|
}
|
||||||
// negative index = first sheet
|
// negative index = first sheet
|
||||||
@@ -44,7 +44,7 @@ func TestWorkbookSheetExport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
js, err := WorkbookSheetJSON(data, 1)
|
js, err := WorkbookSheetJSON(data, 1)
|
||||||
if err != nil || len(js) != 1 || js[0]["city"] != "Lisbon" {
|
if err != nil || len(js) != 1 || js[0]["city"] != "Berlin" {
|
||||||
t.Fatalf("sheet1 json=%#v err=%v", js, err)
|
t.Fatalf("sheet1 json=%#v err=%v", js, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user