single snapshot test
This commit is contained in:
BIN
plugins/.DS_Store
vendored
Normal file
BIN
plugins/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
plugins/owncloud-attach/.DS_Store
vendored
Normal file
BIN
plugins/owncloud-attach/.DS_Store
vendored
Normal file
Binary file not shown.
15
plugins/owncloud-attach/README.md
Normal file
15
plugins/owncloud-attach/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# owncloud-attach plugin
|
||||
|
||||
Reference plugin for the Mailcow Plugin Runtime.
|
||||
|
||||
## What it demonstrates
|
||||
- compose toolbar contribution
|
||||
- compose attachments panel contribution
|
||||
- plugin-hosted UI assets
|
||||
- runtime API contract for file listing and file selection
|
||||
- Mailcow draft-upload handoff metadata
|
||||
|
||||
## API endpoints
|
||||
- `GET /plugins/owncloud-attach/api/me`
|
||||
- `GET /plugins/owncloud-attach/api/files?path=/`
|
||||
- `POST /plugins/owncloud-attach/api/select`
|
||||
7
plugins/owncloud-attach/data/files.json
Normal file
7
plugins/owncloud-attach/data/files.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"items": [
|
||||
{"id": "f-001", "name": "Q2-project-plan.pdf", "size": 183240, "path": "/Shared/Projects/Q2-project-plan.pdf", "mime": "application/pdf"},
|
||||
{"id": "f-002", "name": "brand-assets.zip", "size": 8251042, "path": "/Shared/Brand/brand-assets.zip", "mime": "application/zip"},
|
||||
{"id": "f-003", "name": "mailcow-plugin-runtime-roadmap.md", "size": 12590, "path": "/Engineering/Mailcow/mailcow-plugin-runtime-roadmap.md", "mime": "text/markdown"}
|
||||
]
|
||||
}
|
||||
41
plugins/owncloud-attach/plugin.json
Normal file
41
plugins/owncloud-attach/plugin.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"id": "owncloud-attach",
|
||||
"name": "ownCloud Attachments",
|
||||
"version": "0.2.0",
|
||||
"description": "Adds ownCloud-backed attachment selection for Mailcow compose flows.",
|
||||
"enabledByDefault": true,
|
||||
"entrypoints": {
|
||||
"menu": "/plugins/owncloud-attach/ui",
|
||||
"api": "/plugins/owncloud-attach/api",
|
||||
"ui": "/plugins/owncloud-attach/ui"
|
||||
},
|
||||
"permissions": ["mail.send", "oidc.userinfo", "storage.read"],
|
||||
"hooks": ["mail.compose.toolbar", "mail.compose.attachments"],
|
||||
"contributions": {
|
||||
"mail.compose.toolbar": [
|
||||
{
|
||||
"hook": "mail.compose.toolbar",
|
||||
"type": "button",
|
||||
"id": "toolbar-button",
|
||||
"label": "ownCloud",
|
||||
"icon": "cloud",
|
||||
"description": "Open the ownCloud file picker from the compose toolbar.",
|
||||
"order": 10,
|
||||
"mount": "toolbar.js",
|
||||
"css": "owncloud-attach.css"
|
||||
}
|
||||
],
|
||||
"mail.compose.attachments": [
|
||||
{
|
||||
"hook": "mail.compose.attachments",
|
||||
"type": "panel",
|
||||
"id": "attachments-panel",
|
||||
"label": "Attach from ownCloud",
|
||||
"description": "Mounts a file browser panel for selecting ownCloud files to attach.",
|
||||
"order": 20,
|
||||
"mount": "attachments.js",
|
||||
"css": "owncloud-attach.css"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
92
plugins/owncloud-attach/ui/attachments.js
Normal file
92
plugins/owncloud-attach/ui/attachments.js
Normal file
@@ -0,0 +1,92 @@
|
||||
(function(){
|
||||
const runtime = window.MAILCOW_PLUGIN_RUNTIME;
|
||||
if (!runtime) return;
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (!bytes) return '0 B';
|
||||
const units = ['B','KB','MB','GB'];
|
||||
let value = bytes;
|
||||
let i = 0;
|
||||
while (value >= 1024 && i < units.length - 1) {
|
||||
value /= 1024;
|
||||
i += 1;
|
||||
}
|
||||
return value.toFixed(value >= 10 || i === 0 ? 0 : 1) + ' ' + units[i];
|
||||
}
|
||||
|
||||
runtime.registerMount('owncloud-attach','mail.compose.attachments','attachments-panel', async (target, context) => {
|
||||
const panel = document.createElement('section');
|
||||
panel.className = 'owncloud-panel';
|
||||
panel.innerHTML = '<h3>Attach from ownCloud</h3><div class="owncloud-muted">Loading your files…</div>';
|
||||
target.innerHTML = '';
|
||||
target.appendChild(panel);
|
||||
|
||||
const [meRes, filesRes] = await Promise.all([
|
||||
fetch(context.apiBase + '/me', { credentials: 'include' }),
|
||||
fetch(context.apiBase + '/files?path=/', { credentials: 'include' })
|
||||
]);
|
||||
const me = await meRes.json();
|
||||
const filesPayload = await filesRes.json();
|
||||
const items = filesPayload.items || [];
|
||||
|
||||
panel.innerHTML = '';
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = 'Attach from ownCloud';
|
||||
const info = document.createElement('div');
|
||||
info.className = 'owncloud-muted';
|
||||
info.textContent = 'Signed in as ' + ((me.user && me.user.displayName) || 'Mailcow user') + ' • Source: ' + (me.user && me.user.owncloudBaseUrl || 'ownCloud');
|
||||
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.className = 'owncloud-toolbar';
|
||||
const attachButton = document.createElement('button');
|
||||
attachButton.className = 'owncloud-attach-button';
|
||||
attachButton.textContent = 'Attach selected';
|
||||
attachButton.disabled = true;
|
||||
|
||||
const status = document.createElement('div');
|
||||
status.className = 'owncloud-status';
|
||||
|
||||
const list = document.createElement('ul');
|
||||
list.className = 'owncloud-list';
|
||||
const selected = new Set();
|
||||
|
||||
items.forEach((item) => {
|
||||
const li = document.createElement('li');
|
||||
const left = document.createElement('label');
|
||||
left.style.display = 'flex';
|
||||
left.style.gap = '.5rem';
|
||||
left.style.alignItems = 'center';
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.addEventListener('change', () => {
|
||||
if (checkbox.checked) selected.add(item.id); else selected.delete(item.id);
|
||||
attachButton.disabled = selected.size === 0;
|
||||
});
|
||||
const text = document.createElement('span');
|
||||
text.innerHTML = '<strong>' + item.name + '</strong><div class="owncloud-meta">' + item.path + ' • ' + formatSize(item.size) + '</div>';
|
||||
left.appendChild(checkbox);
|
||||
left.appendChild(text);
|
||||
li.appendChild(left);
|
||||
list.appendChild(li);
|
||||
});
|
||||
|
||||
attachButton.addEventListener('click', async () => {
|
||||
const files = items.filter((item) => selected.has(item.id));
|
||||
const res = await fetch(context.apiBase + '/select', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ files })
|
||||
});
|
||||
const payload = await res.json();
|
||||
status.textContent = 'Queued ' + payload.selectedCount + ' file(s) for Mailcow attachment upload via ' + payload.mode + '.';
|
||||
});
|
||||
|
||||
toolbar.appendChild(attachButton);
|
||||
panel.appendChild(title);
|
||||
panel.appendChild(info);
|
||||
panel.appendChild(toolbar);
|
||||
panel.appendChild(list);
|
||||
panel.appendChild(status);
|
||||
});
|
||||
})();
|
||||
11
plugins/owncloud-attach/ui/owncloud-attach.css
Normal file
11
plugins/owncloud-attach/ui/owncloud-attach.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.mailcow-plugin-target{margin:.5rem 0}
|
||||
.owncloud-attach-button{border:1px solid #444;border-radius:8px;padding:.5rem .8rem;background:#fff;color:#111;cursor:pointer;font:inherit}
|
||||
.owncloud-attach-button:hover{background:#f5f5f5}
|
||||
.owncloud-panel{border:1px solid #d9d9d9;border-radius:12px;padding:1rem;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,.06);max-width:740px}
|
||||
.owncloud-panel h3{margin:.25rem 0 .5rem 0}
|
||||
.owncloud-list{list-style:none;padding:0;margin:.75rem 0}
|
||||
.owncloud-list li{display:flex;justify-content:space-between;gap:1rem;padding:.5rem .75rem;border-bottom:1px solid #eee}
|
||||
.owncloud-meta{color:#666;font-size:.92rem}
|
||||
.owncloud-toolbar{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}
|
||||
.owncloud-status{margin-top:.75rem;color:#155724}
|
||||
.owncloud-muted{color:#666}
|
||||
19
plugins/owncloud-attach/ui/toolbar.js
Normal file
19
plugins/owncloud-attach/ui/toolbar.js
Normal file
@@ -0,0 +1,19 @@
|
||||
(function(){
|
||||
const runtime = window.MAILCOW_PLUGIN_RUNTIME;
|
||||
if (!runtime) return;
|
||||
runtime.registerMount('owncloud-attach','mail.compose.toolbar','toolbar-button', async (target, context) => {
|
||||
target.innerHTML = '';
|
||||
const button = document.createElement('button');
|
||||
button.className = 'owncloud-attach-button';
|
||||
button.textContent = context.contribution.label || 'ownCloud';
|
||||
button.addEventListener('click', async () => {
|
||||
const panelTarget = document.querySelector('[data-plugin-target="owncloud-attach:mail.compose.attachments:attachments-panel"]');
|
||||
if (panelTarget) {
|
||||
panelTarget.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
panelTarget.classList.add('owncloud-flash');
|
||||
setTimeout(() => panelTarget.classList.remove('owncloud-flash'), 1200);
|
||||
}
|
||||
});
|
||||
target.appendChild(button);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user