Documentation
¶
Overview ¶
Package newdoc answers one question: what are the BYTES of a brand-new, empty document of type X?
Why this is not trivial ¶
For a .md or a .csv the answer is "none" — a zero-byte file is a valid, empty text file and every editor opens it. For a .docx it is not: an Office document is a ZIP of XML parts, and a zero-byte file named report.docx is rejected by Word, by OnlyOffice and by LibreOffice alike. The same is true of .xlsx, .pptx and the OpenDocument trio. So "create an empty file" splits into two genuinely different jobs and this package owns the hard one.
Why the bytes are embedded rather than generated ¶
The obvious alternative is to shell out to `soffice --headless --convert-to` at create time. It was rejected because it is not available where the product runs: the full image (ghcr.io/brf-tech/filex:vX) carries LibreOffice, but `:slim` and the bare `filex` binary deliberately carry no external tools at all — that is the documented point of those distributions. A feature that silently works on one image and 500s on another is worse than a feature with a narrower promise, so the bytes come from the binary itself: a few KB of XML, identical on every distribution, with no process to spawn and nothing to be absent.
Why the templates are XML files and not .docx blobs ¶
The parts live under templates/ as readable XML and are zipped in memory on demand. A committed .docx would be an opaque binary in the diff — nobody could review a change to it, and nobody could tell a corrupted one from a deliberate one. As text, every part is reviewable and the ZIP container is reconstructed by code that is itself tested.
⚠ The ZIP is built with an explicit part manifest, NOT by walking the embedded tree. Two of the formats care about entry ORDER: an OpenDocument file must carry `mimetype` as its FIRST entry, STORED (uncompressed) and with no extra field, because that is what puts the media type at byte offset 38 where every magic-byte sniffer looks for it. A lexical walk puts META-INF first and the file stops being recognisable as an ODF document while still being a perfectly valid ZIP — which is exactly the kind of failure that passes every test that only asks "does it unzip?".
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnknownType = errors.New("newdoc: unknown document type")
ErrUnknownType is returned for an extension this build cannot create.
Functions ¶
Types ¶
type AppDoc ¶ added in v0.48.0
type AppDoc struct {
Plugin string `json:"plugin"`
View string `json:"view"`
Label map[string]string `json:"label"`
}
AppDoc is the app behind an app's row of the "New" menu.
type Group ¶
type Group string
Group is the coarse family a type belongs to. It exists for the picker's section headings; it is NOT the capability gate (see Type.Requires).
const ( // GroupDocument — office documents. Real bytes, real editor. GroupDocument Group = "document" // GroupText — text-shaped files where empty is the correct content. GroupText Group = "text" // GroupDiagram — diagram sources. GroupDiagram Group = "diagram" // GroupApp — a kind an installed app adds (`new_documents`): the app's // own row, made and opened by the app, not by this registry. GroupApp Group = "app" )
type Requirement ¶
type Requirement string
Requirement names the external service a client needs before it should offer this type to a person.
⚠ The server states the dependency; it does not resolve it. Whether OnlyOffice is actually reachable is already answered by /api/files/capabilities (external.onlyoffice), and an embedder may point at a document server this process cannot see — so the client is the only place that knows the true answer. What the client must NOT do is re-derive the dependency from a hardcoded extension list of its own: that list would rot the moment this registry grows a type.
const ( // RequiresNothing — a built-in editor handles it (code / markdown). RequiresNothing Requirement = "" // RequiresOnlyOffice — only the OnlyOffice surface can open it. RequiresOnlyOffice Requirement = "onlyoffice" // RequiresDrawio — only the drawio surface can open it. RequiresDrawio Requirement = "drawio" // RequiresApp — an app's interface opens it; the server lists the row // only while the app runs, so there is nothing for a client to resolve. RequiresApp Requirement = "app" )
type Type ¶
type Type struct {
Ext string `json:"ext"`
Group Group `json:"group"`
MIME string `json:"mime"`
Requires Requirement `json:"requires,omitempty"`
// ExtRequired says the file must carry this extension (#56). True for the
// containers — an office document or a diagram is a ZIP or an XML file
// that its editor finds BY extension, so "report" with no extension is a
// document nothing opens. False for text: the new file is zero bytes and
// is just as valid called LICENSE, Makefile or test.conf, so the person
// names it whatever they like.
//
// ⚠ Never `omitempty`. A client tells a server from before #56 by this
// key being ABSENT (that server appends the extension to every type), so
// `false` has to be on the wire.
ExtRequired bool `json:"ext_required"`
// Key is what a create request names when it is not Ext: an app's row
// (`app:<plugin>:<ext>`), whose extension may be one this registry also
// makes. Empty for the registry's own types.
Key string `json:"key,omitempty"`
// App says whose row it is and which of its views opens the new file,
// with the app's own label for it. Nil for the registry's own types.
App *AppDoc `json:"app,omitempty"`
}
Type is one creatable document type, as published to clients.
There is deliberately no label here. Labels are localised, the catalogues live in the frontend package, and a server-rendered English string would be the one piece of this dialog that could not be translated.