saltcorn-samba 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,82 @@
1
+ # Changelog
2
+
3
+ All notable changes to `saltcorn-samba` are documented here.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
+ and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.3.0] – 2026-07-05
8
+
9
+ ### Added
10
+ - **Upload** von Dateien über den `SambaFileManager` — Multi-File-Auswahl,
11
+ Drag-&-Drop-Zone, optionales Überschreiben.
12
+ - **Neuer Ordner** anlegen direkt aus der Toolbar.
13
+ - **Umbenennen** und **Löschen** pro Zeile (Datei oder Ordner; Ordner werden
14
+ rekursiv gelöscht).
15
+ - Neue Plugin-Config-Felder (zweiter Config-Schritt *Access & permissions*):
16
+ - `min_role_write` (Default `40` = Staff+Admin, `100` deaktiviert alle
17
+ Schreibaktionen).
18
+ - `allow_upload`, `allow_delete`, `allow_rename`, `allow_mkdir`
19
+ (jeweils per-Feature einzeln aktivierbar, Default aus).
20
+ - `max_upload_mb` (Default `50`).
21
+ - `denied_extensions` (Blocklist; Default:
22
+ `exe,bat,cmd,com,msi,scr,vbs,js,jse,wsf,wsh,ps1,ps1xml,psm1,sh,bash,zsh`).
23
+ - Neue Routen: `POST /sambaupload`, `POST /sambadelete`, `POST /sambarename`,
24
+ `POST /sambamkdir`.
25
+ - `sanitizeFilename()` — lehnt Slashes, Steuerzeichen, `<>:"|?*`, führende/
26
+ abschließende Punkte oder Leerzeichen, Windows-Reserved-Names (CON, PRN,
27
+ AUX, NUL, COM1–9, LPT1–9) und Namen länger als 255 Zeichen ab.
28
+ - `SambaFileManager`-UI: Toolbar-Buttons *Upload*, *Neuer Ordner*, pro Zeile
29
+ *Umbenennen* / *Löschen*, Modal-Dialoge, Toast-Benachrichtigungen.
30
+ - Erweiterte Sanitizer-Tests (Filenames + Pfade, ~25 Testfälle).
31
+
32
+ ### Changed
33
+ - Plugin-Konfiguration ist jetzt zweistufig: *Samba server* und
34
+ *Access & permissions*. Bestehende `min_role_read`/Server-Felder bleiben
35
+ unverändert.
36
+ - `GET /sambadir` liefert zusätzlich ein `perms`-Objekt (`upload`,
37
+ `delete`, `rename`, `mkdir`), damit die UI Buttons für unberechtigte
38
+ Rollen ausblendet.
39
+ - README erweitert um alle neuen Config-Felder, Routen und UI-Aktionen.
40
+
41
+ ### Security
42
+ - **CSRF-Schutz** auf allen neuen POST-Routen — akzeptiert entweder
43
+ `_csrf` im Body oder Header `X-CSRF-Token` / `CSRF-Token`.
44
+ - Path-Traversal-Prüfung greift auch für Ziel-Namen bei `rename` und `mkdir`.
45
+ - Extension-Blocklist verhindert das Hochladen ausführbarer Dateien.
46
+ - Rollen-Gate `min_role_write` schützt alle Schreiboperationen.
47
+
48
+ ## [0.2.0] – 2026-07-05
49
+
50
+ ### Added
51
+ - **New view template `SambaFileManager`** — a Saltcorn-style file browser
52
+ (like *Settings → Files*). Table with icon, filename, media type, size,
53
+ modified date and per-row actions (View / Download / Open in file manager).
54
+ Includes breadcrumb navigation, up-button, home-button, refresh, hidden-file
55
+ toggle, click-to-sort columns and optional pagination.
56
+ - Inline PDF/image viewer directly inside the file-manager view.
57
+ - Publishing metadata: `repository`, `homepage`, `bugs`, `engines`,
58
+ `publishConfig`, `files`, `prepublishOnly` — ready for `npm publish` and
59
+ GitHub-based installs.
60
+ - MIT `LICENSE` file, `CHANGELOG.md`, `.gitignore`, `.npmignore`.
61
+ - Simple sanitizer unit tests under `test/`.
62
+
63
+ ### Changed
64
+ - Plugin version is now read from `package.json`; the client bootstrap URLs
65
+ (`/plugins/public/saltcorn-samba@<version>/…`) update automatically.
66
+ - View templates are wrapped in `index.js` to inject `__pluginVersion` into
67
+ their configuration – no more hard-coded version strings across files.
68
+
69
+ ### Fixed
70
+ - Path sanitizer now rejects `//srv/share`-style UNC paths *before*
71
+ collapsing consecutive slashes.
72
+ - HTML-escape user-visible path in the `/sambalink` response.
73
+
74
+ ## [0.1.0] – 2026-07-05
75
+
76
+ ### Added
77
+ - Initial release.
78
+ - `SambaTree` view template (lazy directory tree).
79
+ - `samba_pdf` fieldview (inline PDF/image viewer for String fields).
80
+ - Routes `/sambadir`, `/sambafile`, `/sambalink`.
81
+ - Direct SMB2 access via `@marsaud/smb2`.
82
+ - Strict path-traversal protection.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peter Vassen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # saltcorn-samba
2
+
3
+ Ein Saltcorn-Plugin für den Zugriff auf einen **Samba-/CIFS-Server (SMB2/3)**
4
+ direkt aus dem Browser — ohne Mount, ohne `smbclient` im Container.
5
+
6
+ Features:
7
+
8
+ - 📂 **`SambaFileManager`-View** — Datei-Manager wie *Einstellungen → Dateien* in Saltcorn: Tabelle mit Symbol, Name, MIME-Typ, Größe, Änderungsdatum und Aktionen. Breadcrumb-Navigation, Up-/Home-/Refresh-Button, Sortierung per Spaltenklick, Pagination, Anzeige versteckter Dateien optional.
9
+ - ⬆️ **Upload / Neuer Ordner / Umbenennen / Löschen** — optional aktivierbar,
10
+ Multi-File-Upload mit Drag-&-Drop, Toast-Feedback, Rollen-Gate.
11
+ - 🌳 **`SambaTree`-View** — Lazy-loading Verzeichnisbaum, ideal zum Einbetten in Show-Views.
12
+ - 📄 **`samba_pdf`-Fieldview** — String-Feld mit Datei-Pfad → PDF/Bild inline im Browser, plus Download- und externe-App-Buttons.
13
+ - 🚀 **`smb://`-Links** — Öffnet Dateien und Ordner direkt in Nemo, Nautilus, Dolphin (Linux) oder Explorer (Windows).
14
+ - 🔒 **Sicherheit** — Base-Path als „Chroot", strenge Path-Traversal-Prüfung (`..`, absolute Pfade, UNC, Drive-Letters, NUL-Bytes), CSRF-Schutz auf Schreib-Routen, Filename-Sanitizer, Extension-Blocklist, rollenbasierter Zugriff (getrennte Lese-/Schreib-Rollen).
15
+ - 🐳 **Docker-freundlich** — direkt per SMB2-Protokoll, keine System-Binaries nötig.
16
+
17
+ ---
18
+
19
+ ## Screenshots
20
+
21
+ Der `SambaFileManager` ist optisch am eingebauten Saltcorn-File-Browser
22
+ orientiert: dieselben Spalten (Filename, Media type, Size, Modified) und
23
+ Aktionsknöpfe.
24
+
25
+ ```
26
+ ┌─────────────────────────────────────────────────────────────────────┐
27
+ │ Samba files 3 folders · 12 files │
28
+ ├─────────────────────────────────────────────────────────────────────┤
29
+ │ ⬆ Up 🏠 ↻ / kunden / akte-42 ☐ Show hidden │
30
+ ├─────────────────────────────────────────────────────────────────────┤
31
+ │ Filename ▲ Media type Size Modified Actions │
32
+ │ 📁 vertraege folder 2026-06-01 [Open] │
33
+ │ 📁 rechnungen folder 2026-06-15 [Open] │
34
+ │ 📄 anschreiben.pdf application/pdf 84 KiB 2026-06-20 [View] [DL] ↗│
35
+ │ 🖼️ logo.png image/png 12 KiB 2026-05-11 [View] [DL] ↗│
36
+ └─────────────────────────────────────────────────────────────────────┘
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ ### 1. Über die Saltcorn-Plugin-Verwaltung (empfohlen)
44
+
45
+ Sobald das Plugin auf npm veröffentlicht ist:
46
+
47
+ 1. Einstellungen → Plugins → „…" → *Add another plugin*
48
+ 2. Name: `saltcorn-samba`
49
+ 3. Source: **npm**
50
+ 4. Location: `saltcorn-samba`
51
+ 5. Version leer lassen (aktuellste) oder z. B. `0.3.0`
52
+
53
+ ### 2. Direkt aus GitHub
54
+
55
+ 1. Einstellungen → Plugins → „…" → *Add another plugin*
56
+ 2. Name: `saltcorn-samba`
57
+ 3. Source: **github**
58
+ 4. Location: `pv-host/saltcorn-samba`
59
+
60
+ ### 3. Lokale Installation (Entwicklung)
61
+
62
+ ```bash
63
+ git clone https://github.com/pv-host/saltcorn-samba.git
64
+ cd saltcorn-samba
65
+ npm install
66
+ saltcorn install-plugin -d "$PWD"
67
+ ```
68
+
69
+ Alternativ manuell via UI: Source = `local`, Location = absoluter Pfad.
70
+
71
+ ### 4. Docker
72
+
73
+ Plugin-Ordner als Volume in den Container einbinden:
74
+
75
+ ```yaml
76
+ services:
77
+ saltcorn:
78
+ image: saltcorn/saltcorn:latest
79
+ volumes:
80
+ - ./plugins/saltcorn-samba:/opt/plugins/saltcorn-samba
81
+ # Der SMB-Port des Samba-Servers muss aus dem Container erreichbar sein.
82
+ ```
83
+
84
+ Anschließend in Saltcorn als *local* Plugin mit
85
+ `Location = /opt/plugins/saltcorn-samba` registrieren.
86
+
87
+ Verbindung testen:
88
+
89
+ ```bash
90
+ docker exec -it <saltcorn-container> \
91
+ node -e "require('net').createConnection(445,'IP-DES-SAMBA').on('connect',()=>{console.log('ok');process.exit()}).on('error',e=>{console.error(e.message);process.exit(1)})"
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Konfiguration
97
+
98
+ Nach der Installation unter *Einstellungen → Plugins → saltcorn-samba →
99
+ Configure* ausfüllen. Die Konfiguration ist zweistufig:
100
+
101
+ ### Schritt 1 — *Samba server*
102
+
103
+ | Feld | Beispiel | Beschreibung |
104
+ |---|---|---|
105
+ | Server | `192.168.1.10` | Hostname oder IP des Samba-Servers |
106
+ | Share name | `documents` | Name des Shares (ohne Slashes) |
107
+ | Domain / Workgroup | `WORKGROUP` | optional |
108
+ | Username | `saltcorn-user` | SMB-Benutzer |
109
+ | Password | *(secret)* | wird im Saltcorn-Konfigstore gespeichert |
110
+ | Base path | `projects` | *optional* — beschränkt jeden Zugriff auf dieses Unterverzeichnis |
111
+ | Port | `445` | Standard-SMB2-Port |
112
+ | SMB host visible to clients | `fileserver.lan` | *optional* — Host, der in `smb://`-Links auftaucht (nützlich in Docker) |
113
+
114
+ ### Schritt 2 — *Access & permissions*
115
+
116
+ | Feld | Default | Beschreibung |
117
+ |---|---|---|
118
+ | Minimum role to read files | `80` | 1=Admin, 40=Staff, 80=User, 100=public |
119
+ | Minimum role to write files | `40` | `100` deaktiviert Schreibaktionen komplett |
120
+ | Allow upload | *aus* | schaltet Upload-Button + `POST /sambaupload` frei |
121
+ | Allow delete | *aus* | schaltet Löschen frei (`POST /sambadelete`) |
122
+ | Allow rename | *aus* | schaltet Umbenennen frei (`POST /sambarename`) |
123
+ | Allow mkdir | *aus* | schaltet *Neuer Ordner* frei (`POST /sambamkdir`) |
124
+ | Max. Upload-Größe (MB) | `50` | Limit pro Datei |
125
+ | Denied file extensions | `exe,bat,cmd,com,msi,scr,vbs,js,jse,wsf,wsh,ps1,ps1xml,psm1,sh,bash,zsh` | kommagetrennte Blocklist |
126
+
127
+ **Empfehlung:**
128
+
129
+ - Nur die Features aktivieren, die wirklich gebraucht werden.
130
+ - Auf dem Samba-Server einen separaten Nutzer mit passenden Rechten anlegen
131
+ (read-only wenn nur gelesen werden soll).
132
+ - Zusätzlich mit *Base path* den Zugriff auf ein Unterverzeichnis begrenzen.
133
+
134
+ ---
135
+
136
+ ## Verwendung
137
+
138
+ ### View: `SambaFileManager`
139
+
140
+ Menu → Views → *Create view* → Template `SambaFileManager`.
141
+
142
+ Optionen:
143
+
144
+ | Option | Zweck |
145
+ |---|---|
146
+ | **Root directory mode** | `static` = immer Base-Path aus Plugin-Config, `from_field` = Sub-Pfad kommt aus einem DB-Feld der aktuellen Zeile |
147
+ | **Row field with sub-path** | Feld der Tabelle mit dem relativen Ordnernamen (bei `from_field`) |
148
+ | **Extra sub-path** | statischer Suffix (z. B. `invoices`) |
149
+ | **Show hidden files** | `.dotfiles` einblenden |
150
+ | **Allow navigating up** | Up-Button aktivieren (nur bis zum Root, kein Ausbruch) |
151
+ | **Open PDFs / images inline** | Klick öffnet die Datei im integrierten Viewer |
152
+ | **Show "Open in file manager" button** | `smb://`-Link pro Zeile |
153
+ | **Page size** | Einträge pro Seite (0 = alle) |
154
+ | **Panel title** | Text im Karten-Header |
155
+
156
+ Beispiel-Datenmodell für kunden-spezifische Ordner:
157
+
158
+ ```
159
+ Tabelle: kunden
160
+ Felder: name (String), akte_dir (String, z. B. "kunde_42/2026")
161
+ ```
162
+
163
+ Dann eine Show-View von `kunden` bauen und die `SambaFileManager`-View mit
164
+ Mode = `from_field`, Row field = `akte_dir` einbetten.
165
+
166
+ ### View: `SambaTree`
167
+
168
+ Kompaktere Alternative – lazy-loading Baum, ideal in einer Sidebar oder
169
+ neben Formularen. Gleiche Path-Modi wie beim File-Manager.
170
+
171
+ ### Fieldview `samba_pdf`
172
+
173
+ Für ein String-Feld mit Datei-Pfad (relativ zum Base-Path). In der View-
174
+ Konfiguration `Field view = samba_pdf` wählen. Rendert PDFs im `<iframe>`,
175
+ Bilder als `<img>`, alles andere als Download-Buttons.
176
+
177
+ ### smb://-Links
178
+
179
+ Jeder Datei-/Ordner-Eintrag hat einen ↗-Button. Klick öffnet die Route
180
+ `/sambalink`, die eine kleine HTML-Zwischenseite mit `smb://`-Anker rendert.
181
+
182
+ - Linux: GNOME/Cinnamon/KDE → Nautilus / Nemo / Dolphin
183
+ - Windows: Explorer
184
+ - macOS: Finder
185
+
186
+ *Hinweis:* Chromium blockiert direkte `smb://`-Redirects, deshalb die
187
+ Zwischenseite mit sichtbarem Klick-Link.
188
+
189
+ ---
190
+
191
+ ## Routen (öffentliche HTTP-API des Plugins)
192
+
193
+ ### Lesen (GET, Rolle ≥ `min_role_read`)
194
+
195
+ | Methode + URL | Parameter | Zweck |
196
+ |---|---|---|
197
+ | `GET /sambadir` | `path`, `show_hidden` | JSON-Verzeichnisliste + `perms` |
198
+ | `GET /sambafile` | `path`, `disposition=inline\|attachment` | Datei-Stream |
199
+ | `GET /sambalink` | `path` | HTML-Seite mit `smb://`-Anker |
200
+
201
+ ### Schreiben (POST, Rolle ≥ `min_role_write`, CSRF-Token erforderlich)
202
+
203
+ | Methode + URL | Body / Form | Zweck |
204
+ |---|---|---|
205
+ | `POST /sambaupload` | `multipart/form-data`: `dir`, `file` (n-fach), `overwrite`, `_csrf` | Dateien hochladen |
206
+ | `POST /sambadelete` | JSON: `path`, `_csrf` | Datei/Ordner (rekursiv) löschen |
207
+ | `POST /sambarename` | JSON: `path`, `new_name`, `_csrf` | Umbenennen |
208
+ | `POST /sambamkdir` | JSON: `dir`, `name`, `_csrf` | Neuen Ordner anlegen |
209
+
210
+ Das CSRF-Token wird von Saltcorn per `req.csrfToken()` erzeugt. Der Client
211
+ übergibt es entweder als Feld `_csrf` im Body oder als HTTP-Header
212
+ `X-CSRF-Token` (bzw. `CSRF-Token`). Wer CSRF für diese Routen abschalten
213
+ möchte, kann das in den Saltcorn-*Users & Security*-Einstellungen tun.
214
+
215
+ Alle Routen prüfen die Rolle, validieren jeden Pfad gegen Path-Traversal
216
+ und lehnen Filenames ab, die Slashes, Steuerzeichen, `<>:"|?*`,
217
+ führende/abschließende Punkte oder Windows-Reserved-Names enthalten.
218
+
219
+ ---
220
+
221
+ ## Sicherheit
222
+
223
+ - **Base-Path als Chroot** — sanitizer verhindert das Verlassen.
224
+ - **Path-Prüfung** — `..`, absolute Pfade (`/foo`), UNC (`//srv/share`,
225
+ `\\srv\share`), Drive-Letters (`C:`, `d:\`), NUL-Bytes werden abgelehnt.
226
+ - **Filename-Sanitizer** für Upload / Rename / Mkdir — lehnt Slashes,
227
+ Steuerzeichen, `<>:"|?*`, führende/abschließende Punkte, Leerzeichen und
228
+ Windows-Reserved-Names (`CON`, `PRN`, `AUX`, `NUL`, `COM1–9`, `LPT1–9`)
229
+ ab.
230
+ - **CSRF-Schutz** auf allen POST-Routen (Body `_csrf` oder Header
231
+ `X-CSRF-Token`).
232
+ - **Extension-Blocklist** für Uploads (Default blockt exe/bat/cmd/vbs/js
233
+ u.ä.), konfigurierbar.
234
+ - **Getrennte Rollen-Gates** — `min_role_read` und `min_role_write`, plus
235
+ Feature-Toggles pro Schreib-Aktion.
236
+ - **Kein öffentliches Caching** — Files werden mit `Cache-Control: no-store`
237
+ ausgeliefert.
238
+ - **HTML-Escape** in allen serverseitig gerenderten Antworten.
239
+
240
+ Unit-Tests zum Path- und Filename-Sanitizer laufen in CI (`npm test`).
241
+
242
+ ---
243
+
244
+ ## Entwicklung
245
+
246
+ ```bash
247
+ npm install
248
+ npm run lint # Syntax-Check aller JS-Dateien
249
+ npm test # Sanitizer-Tests
250
+ ```
251
+
252
+ Lokale Saltcorn-Instanz mit Live-Reload:
253
+
254
+ ```bash
255
+ saltcorn install-plugin -d "$PWD"
256
+ SALTCORN_NWORKERS=1 saltcorn serve --dev
257
+ ```
258
+
259
+ Anschließend Änderungen an `index.js`/`filemanager-view.js`/etc. → Server
260
+ lädt neu.
261
+
262
+ ### Als npm-Paket veröffentlichen
263
+
264
+ ```bash
265
+ # In package.json: version bumpen, CHANGELOG ergänzen
266
+ npm login
267
+ npm run lint && npm test # läuft auch als prepublishOnly
268
+ npm publish # --access public wird durch publishConfig gesetzt
269
+ git tag v0.3.0 && git push --tags
270
+ ```
271
+
272
+ ### Als GitHub-Plugin nutzbar machen
273
+
274
+ Nach `git push` auf `main`/`master` können Saltcorn-Nutzer das Plugin ohne
275
+ npm direkt installieren:
276
+
277
+ ```
278
+ Source: github
279
+ Location: pv-host/saltcorn-samba
280
+ ```
281
+
282
+ Saltcorn führt `npm install` im Plugin-Ordner automatisch aus.
283
+
284
+ ### Ins Saltcorn-Store-Verzeichnis eintragen
285
+
286
+ 1. Auf https://store.saltcorn.com/ einloggen (Admin-Rolle nötig)
287
+ 2. „Add extension" → Name `saltcorn-samba`, Source `npm`, Location `saltcorn-samba`
288
+ 3. Beschreibung + Kategorie eintragen
289
+
290
+ Ab dann taucht das Plugin im Plugins-Store jeder Saltcorn-Instanz auf.
291
+
292
+ ---
293
+
294
+ ## Bekannte Grenzen
295
+
296
+ - Dateien werden komplett gepuffert (`readFile` / `writeFile`). Für
297
+ Dateien > 100 MB besser einen CIFS-Mount + Saltcorn-`File`-Typ nutzen.
298
+ - **Nur SMB2/3** — SMBv1 wird nicht unterstützt.
299
+ - **Ein SMB-User** entscheidet über Sichtbarkeit und Schreibrechte auf
300
+ dem Share — für rollen­spezifische Sichtbarkeit auf Share-Ebene
301
+ arbeiten oder mehrere Plugin-Instanzen verwenden.
302
+ - **Keine DB-Verknüpfung** von SMB-Dateien (geplant für spätere Version).
303
+
304
+ ---
305
+
306
+ ## Changelog
307
+
308
+ Siehe [CHANGELOG.md](CHANGELOG.md).
309
+
310
+ ---
311
+
312
+ ## Lizenz
313
+
314
+ MIT © 2026 Peter Vassen
@@ -0,0 +1,249 @@
1
+ /**
2
+ * View template: `SambaFileManager`
3
+ *
4
+ * Renders a Saltcorn-style file browser (like Settings → Files) against a
5
+ * Samba/CIFS share. The UI shows a table with columns: icon, name, media
6
+ * type, size, modified, actions. Directories are navigable via click, and
7
+ * the current location is shown as a breadcrumb path.
8
+ *
9
+ * All directory data is fetched lazily from `/sambadir` (defined in the
10
+ * plugin's routes) so the view itself just renders a shell that boots the
11
+ * client-side script.
12
+ */
13
+
14
+ const { div, span, script } = require("@saltcorn/markup/tags");
15
+ const Workflow = require("@saltcorn/data/models/workflow");
16
+ const Form = require("@saltcorn/data/models/form");
17
+ const Field = require("@saltcorn/data/models/field");
18
+ const Table = require("@saltcorn/data/models/table");
19
+
20
+ const { sanitizeRelativePath } = require("./smb-client");
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Configuration workflow
24
+ // ---------------------------------------------------------------------------
25
+
26
+ const configuration_workflow = () =>
27
+ new Workflow({
28
+ steps: [
29
+ {
30
+ name: "File manager options",
31
+ form: async (context) => {
32
+ const table = context.table_id
33
+ ? await Table.findOne({ id: context.table_id })
34
+ : null;
35
+ const fields = table ? await table.getFields() : [];
36
+ const stringFieldOptions = fields
37
+ .filter(
38
+ (f) =>
39
+ f.type &&
40
+ (f.type.name === "String" ||
41
+ f.type === "String" ||
42
+ f.type.name === "Text")
43
+ )
44
+ .map((f) => f.name);
45
+ return new Form({
46
+ fields: [
47
+ new Field({
48
+ name: "mode",
49
+ label: "Root directory mode",
50
+ type: "String",
51
+ required: true,
52
+ attributes: {
53
+ options: ["static", "from_field"],
54
+ },
55
+ sublabel:
56
+ "static = always the plugin base_path. from_field = append a row field value to base_path (only when the view is embedded in a Show view).",
57
+ default: "static",
58
+ }),
59
+ new Field({
60
+ name: "path_field",
61
+ label: "Row field with sub-path",
62
+ type: "String",
63
+ attributes: { options: stringFieldOptions },
64
+ showIf: { mode: "from_field" },
65
+ }),
66
+ new Field({
67
+ name: "extra_subpath",
68
+ label: "Extra sub-path (appended)",
69
+ type: "String",
70
+ sublabel:
71
+ "Optional. Static suffix appended after the field value. Example: 'invoices'",
72
+ }),
73
+ new Field({
74
+ name: "show_hidden",
75
+ label: "Show hidden files",
76
+ type: "Bool",
77
+ }),
78
+ new Field({
79
+ name: "allow_navigate_up",
80
+ label: "Allow navigating up (until the root)",
81
+ type: "Bool",
82
+ default: true,
83
+ }),
84
+ new Field({
85
+ name: "pdf_inline",
86
+ label: "Open PDFs / images inline",
87
+ type: "Bool",
88
+ default: true,
89
+ }),
90
+ new Field({
91
+ name: "expose_smb_link",
92
+ label: "Show 'Open in file manager' button (smb://)",
93
+ type: "Bool",
94
+ default: true,
95
+ }),
96
+ new Field({
97
+ name: "page_size",
98
+ label: "Page size",
99
+ type: "Integer",
100
+ default: 100,
101
+ sublabel:
102
+ "How many entries to show per page. 0 = show all (be careful with huge directories).",
103
+ }),
104
+ new Field({
105
+ name: "title",
106
+ label: "Panel title",
107
+ type: "String",
108
+ default: "Samba files",
109
+ }),
110
+ ],
111
+ });
112
+ },
113
+ },
114
+ ],
115
+ });
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Helpers
119
+ // ---------------------------------------------------------------------------
120
+
121
+ async function get_state_fields() {
122
+ return [];
123
+ }
124
+
125
+ function computeStartPath(configuration, row) {
126
+ const parts = [];
127
+ if (configuration.mode === "from_field" && configuration.path_field && row) {
128
+ const v = row[configuration.path_field];
129
+ if (v) parts.push(String(v));
130
+ }
131
+ if (configuration.extra_subpath) parts.push(String(configuration.extra_subpath));
132
+ return sanitizeRelativePath(parts.join("/"));
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Shell renderer
137
+ // ---------------------------------------------------------------------------
138
+
139
+ function renderShell(startPath, configuration, pluginVersion, csrfToken) {
140
+ const id = "samba-fm-" + Math.random().toString(36).slice(2, 10);
141
+ const opts = {
142
+ startPath,
143
+ showHidden: !!configuration.show_hidden,
144
+ allowNavigateUp: configuration.allow_navigate_up !== false,
145
+ pdfInline: configuration.pdf_inline !== false,
146
+ exposeSmbLink: configuration.expose_smb_link !== false,
147
+ pageSize: Number(configuration.page_size) || 0,
148
+ title: configuration.title || "Samba files",
149
+ csrfToken: csrfToken || "",
150
+ };
151
+
152
+ return (
153
+ div(
154
+ { class: "samba-fm card" },
155
+ div(
156
+ { class: "card-header d-flex align-items-center" },
157
+ span({ class: "samba-fm-title fw-bold" }, opts.title),
158
+ span({ class: "samba-fm-spacer flex-grow-1" }),
159
+ span({ id: id + "-count", class: "text-muted small" })
160
+ ),
161
+ div(
162
+ { class: "card-body p-0" },
163
+ div({ id: id + "-toolbar", class: "samba-fm-toolbar p-2 border-bottom" }),
164
+ div({ id: id + "-list", class: "samba-fm-list" }),
165
+ div({ id: id + "-viewer", class: "samba-fm-viewer" })
166
+ )
167
+ ) +
168
+ script(
169
+ `(function(){
170
+ function boot(){window.saltcornSambaMountFM && window.saltcornSambaMountFM(${JSON.stringify(
171
+ id
172
+ )}, ${JSON.stringify(opts)});}
173
+ if(window.saltcornSambaMountFM){boot();return;}
174
+ var s=document.createElement('script');
175
+ s.src='/plugins/public/saltcorn-samba@${pluginVersion}/samba-filemanager.js';
176
+ s.onload=boot;
177
+ document.head.appendChild(s);
178
+ })();`
179
+ )
180
+ );
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // View functions
185
+ // ---------------------------------------------------------------------------
186
+
187
+ function extractCsrf(extra) {
188
+ const req = extra && extra.req;
189
+ if (req && typeof req.csrfToken === "function") {
190
+ try {
191
+ return req.csrfToken();
192
+ } catch (_) {
193
+ return "";
194
+ }
195
+ }
196
+ return "";
197
+ }
198
+
199
+ async function run(table_id, viewname, configuration, state, extra) {
200
+ const pluginVersion = configuration.__pluginVersion || "0.3.0";
201
+ const csrf = extractCsrf(extra);
202
+ let row = extra && extra.row;
203
+ if (!row && configuration.mode === "from_field" && table_id) {
204
+ const table = await Table.findOne({ id: table_id });
205
+ if (table && state && Object.keys(state).length) {
206
+ try {
207
+ row = await table.getRow(state);
208
+ } catch (_) {
209
+ row = null;
210
+ }
211
+ }
212
+ }
213
+ let startPath = "";
214
+ try {
215
+ startPath = computeStartPath(configuration, row || {});
216
+ } catch (e) {
217
+ return div({ class: "alert alert-danger" }, "Samba file manager: " + e.message);
218
+ }
219
+ return renderShell(startPath, configuration, pluginVersion, csrf);
220
+ }
221
+
222
+ async function runMany(table_id, viewname, configuration, state, extra) {
223
+ const pluginVersion = configuration.__pluginVersion || "0.3.0";
224
+ const csrf = extractCsrf(extra);
225
+ const rows = (extra && extra.rows) || [];
226
+ return rows.map((row) => {
227
+ let startPath = "";
228
+ try {
229
+ startPath = computeStartPath(configuration, row);
230
+ } catch (_) {
231
+ startPath = "";
232
+ }
233
+ return {
234
+ html: renderShell(startPath, configuration, pluginVersion, csrf),
235
+ row,
236
+ };
237
+ });
238
+ }
239
+
240
+ module.exports = {
241
+ name: "SambaFileManager",
242
+ display_state_form: false,
243
+ configuration_workflow,
244
+ get_state_fields,
245
+ run,
246
+ runMany,
247
+ description:
248
+ "Saltcorn-style file browser for a Samba/CIFS share. Table with icon, name, type, size, modified date, and actions.",
249
+ };