Move Custom Scripts out of the repo and add an Import Script picker
Custom Scripts (the Scheduler page's inline editor) used to save into the git-tracked Custom/ folder, so anything saved there would end up on GitHub. They now live in /boot/config/plugins/user.scripts/Varaverk/Scripts, same folder family as Unraid's own User Scripts plugin. Import Script lets you browse the whole server and move an existing script in instead of only creating new ones inline — always a move, never a copy, so no stray duplicate is left where it came from.
This commit is contained in:
+14
-2
@@ -73,8 +73,8 @@ to Community Applications.
|
||||
|
||||
## ━━━ SCRIPTS DIRECTORY SETTING ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
`SCRIPTS_DIR` is the only plugin-level setting. It tells the plugin where to find the
|
||||
Configurations directory and all scripts.
|
||||
`SCRIPTS_DIR` tells the plugin where to find the Configurations directory and all
|
||||
repo scripts.
|
||||
|
||||
**Set it via:** Settings → Other Settings → Varaverk → Scripts directory
|
||||
|
||||
@@ -88,6 +88,18 @@ SCRIPTS_DIR="/boot/config/plugins/varaverk"
|
||||
|
||||
All other configuration lives in `Configurations/master.conf` and `Configurations/host*.conf`.
|
||||
|
||||
`CUSTOM_SCRIPTS_DIR` is a separate, optional override for where the Scheduler tab's
|
||||
Custom Scripts feature reads/writes user-authored scripts (see Manual.md → Custom
|
||||
Scripts). It's intentionally **not** under `SCRIPTS_DIR` — Custom Scripts are personal,
|
||||
non-repo tooling and must never end up inside the git-tracked plugin folder.
|
||||
|
||||
Default: `/boot/config/plugins/user.scripts/Varaverk/Scripts`
|
||||
|
||||
```bash
|
||||
# in varaverk.cfg, alongside SCRIPTS_DIR
|
||||
CUSTOM_SCRIPTS_DIR="/boot/config/plugins/user.scripts/Varaverk/Scripts"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ━━━ REPO MOVE PROCEDURE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
// Import Script — lets the Scheduler page's "+ Import Script" browser move an existing
|
||||
// .sh file from anywhere on the server into CUSTOM_SCRIPTS_DIR. This is a MOVE: the
|
||||
// source is deleted once the copy is verified, so no stale duplicate is left behind.
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-store, no-cache');
|
||||
require_once dirname(__DIR__) . '/include/config.php';
|
||||
|
||||
// ── browse (GET): list subdirectories and .sh files at $path, rooted at / ─────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET' && ($_GET['action'] ?? '') === 'browse') {
|
||||
$path = trim($_GET['path'] ?? '/');
|
||||
if (!preg_match('#^/[^\0]*$#', $path) || str_contains($path, '..')) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid path']);
|
||||
exit;
|
||||
}
|
||||
$clean = rtrim($path, '/') ?: '/';
|
||||
if (!is_dir($clean)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Not a directory: ' . $clean]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$dirOut = shell_exec('find ' . escapeshellarg($clean) . ' -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort | head -300') ?: '';
|
||||
$dirs = array_values(array_filter(array_map('trim', explode("\n", $dirOut))));
|
||||
|
||||
$fileOut = shell_exec('find ' . escapeshellarg($clean) . ' -maxdepth 1 -mindepth 1 -type f -iname "*.sh" 2>/dev/null | sort | head -300') ?: '';
|
||||
$files = array_values(array_filter(array_map('trim', explode("\n", $fileOut))));
|
||||
|
||||
$parent = ($clean !== '/') ? (dirname($clean) ?: '/') : null;
|
||||
echo json_encode(['ok' => true, 'path' => $clean, 'dirs' => $dirs, 'files' => $files, 'parent' => $parent]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── import (POST): move the chosen .sh file into CUSTOM_SCRIPTS_DIR ───────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'import') {
|
||||
$src = trim($_POST['path'] ?? '');
|
||||
if (!preg_match('#^/[^\0]*\.sh$#i', $src) || str_contains($src, '..')) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid script path']);
|
||||
exit;
|
||||
}
|
||||
if (!is_file($src)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Not found: ' . $src]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$srcReal = realpath($src);
|
||||
if ($srcReal === false) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Could not resolve path']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Refuse to move a file out of the git-tracked repo — that would delete a
|
||||
// tracked file out from under git without a commit recording it.
|
||||
$repoReal = realpath(SCRIPTS_DIR);
|
||||
if ($repoReal && str_starts_with($srcReal, $repoReal . '/')) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Refusing to import from inside the Varaverk repo — that would delete a git-tracked file.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Already there — nothing to do.
|
||||
$customReal = realpath(CUSTOM_SCRIPTS_DIR) ?: CUSTOM_SCRIPTS_DIR;
|
||||
if (str_starts_with($srcReal, rtrim($customReal, '/') . '/')) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Already in Custom Scripts.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!is_dir(CUSTOM_SCRIPTS_DIR)) mkdir(CUSTOM_SCRIPTS_DIR, 0755, true);
|
||||
|
||||
$name = basename($srcReal);
|
||||
$dest = CUSTOM_SCRIPTS_DIR . '/' . $name;
|
||||
if (file_exists($dest)) {
|
||||
echo json_encode(['ok' => false, 'error' => "A script named \"$name\" already exists in Custom Scripts."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Copy across filesystems, verify, THEN delete the source — never remove the
|
||||
// only copy on a failed or partial copy.
|
||||
if (!copy($srcReal, $dest)) {
|
||||
@unlink($dest);
|
||||
echo json_encode(['ok' => false, 'error' => 'Copy failed']);
|
||||
exit;
|
||||
}
|
||||
if (filesize($srcReal) !== filesize($dest) || hash_file('sha256', $srcReal) !== hash_file('sha256', $dest)) {
|
||||
@unlink($dest);
|
||||
echo json_encode(['ok' => false, 'error' => 'Copy verification failed — source left untouched']);
|
||||
exit;
|
||||
}
|
||||
chmod($dest, 0755);
|
||||
|
||||
if (!@unlink($srcReal)) {
|
||||
// Copied and verified but couldn't remove the original (permissions, read-only
|
||||
// mount). The script is usable from its new home either way — surface a warning
|
||||
// rather than failing the import outright.
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'id' => 'Custom/' . $name,
|
||||
'warning' => 'Imported, but could not delete the original at ' . $srcReal . ' — remove it manually.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['ok' => true, 'id' => 'Custom/' . $name]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid request']);
|
||||
@@ -8,7 +8,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Invalid id']);
|
||||
exit;
|
||||
}
|
||||
$path = SCRIPTS_DIR . '/' . $id;
|
||||
$path = CUSTOM_SCRIPTS_DIR . '/' . substr($id, strlen('Custom/'));
|
||||
echo json_encode(['ok' => true, 'content' => file_exists($path) ? file_get_contents($path) : '']);
|
||||
exit;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
$id = 'Custom/' . $name . '.sh';
|
||||
$path = SCRIPTS_DIR . '/Custom/' . $name . '.sh';
|
||||
$path = CUSTOM_SCRIPTS_DIR . '/' . $name . '.sh';
|
||||
|
||||
if ($action === 'delete') {
|
||||
if (!file_exists($path)) {
|
||||
@@ -40,7 +40,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
exit;
|
||||
}
|
||||
|
||||
$dir = SCRIPTS_DIR . '/Custom';
|
||||
$dir = CUSTOM_SCRIPTS_DIR;
|
||||
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
||||
if (file_put_contents($path, $content) === false) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Failed to write script']);
|
||||
|
||||
@@ -11,6 +11,11 @@ define('DEPLOY_DIR', SCRIPTS_DIR . '/Deployment');
|
||||
define('DATA_DIR', SCRIPTS_DIR . '/data');
|
||||
define('STATE_DIR', SCRIPTS_DIR . '/State_Files');
|
||||
define('LOG_DIR', '/var/log/varaverk');
|
||||
// User-authored custom scripts (scheduler page "+ Create Script") — kept outside the git
|
||||
// repo entirely, alongside the User Scripts plugin's own storage. Any *.sh file placed
|
||||
// directly in this folder is auto-detected and listed — it doesn't have to be created
|
||||
// through the page's editor.
|
||||
define('CUSTOM_SCRIPTS_DIR', $_vv_cfg['CUSTOM_SCRIPTS_DIR'] ?? '/boot/config/plugins/user.scripts/Varaverk/Scripts');
|
||||
unset($_vv_cfg);
|
||||
|
||||
define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
|
||||
|
||||
@@ -97,7 +97,10 @@ function vv_cron_rebuild(array $schedule): bool {
|
||||
$id = $entry['id'];
|
||||
// When a child's orch is enabled it is the sole trigger — suppress independent cron.
|
||||
if (isset($childToOrch[$id]) && !empty($schedule[$childToOrch[$id]]['enabled'])) continue;
|
||||
$script = "$scriptsDir/$id";
|
||||
// Custom Scripts live outside the repo (CUSTOM_SCRIPTS_DIR) — everything else resolves under SCRIPTS_DIR.
|
||||
$script = str_starts_with($id, 'Custom/')
|
||||
? CUSTOM_SCRIPTS_DIR . '/' . substr($id, strlen('Custom/'))
|
||||
: "$scriptsDir/$id";
|
||||
$flags = !empty($entry['log_enabled']) ? ' --log' : '';
|
||||
$lines[] = "{$entry['cron']} bash \"$runner\" \"$id\" \"$script\"$flags";
|
||||
}
|
||||
@@ -278,6 +281,10 @@ function vv_tools_scripts(): array {
|
||||
return $scripts;
|
||||
}
|
||||
|
||||
// Lists every Custom Script for the scheduler page. Discovery is glob-based, not a
|
||||
// registry — any *.sh file dropped directly into CUSTOM_SCRIPTS_DIR (or a platform
|
||||
// adapter's own Custom/ folder) shows up here, whether or not it was created via the
|
||||
// page's "+ Create Script" editor or has a schedule.json entry yet.
|
||||
function vv_custom_scripts(): array {
|
||||
$schedule = vv_schedule_load();
|
||||
$scripts = [];
|
||||
@@ -297,7 +304,7 @@ function vv_custom_scripts(): array {
|
||||
}
|
||||
};
|
||||
|
||||
$collect(SCRIPTS_DIR . '/Custom', 'Custom/');
|
||||
$collect(CUSTOM_SCRIPTS_DIR, 'Custom/');
|
||||
|
||||
// Platform adapter custom scripts (Plugin/<platform>/Custom/)
|
||||
foreach (glob(SCRIPTS_DIR . '/Plugin/*/Custom') ?: [] as $customDir) {
|
||||
|
||||
@@ -272,7 +272,7 @@ $runningScripts = array_unique($runningScripts);
|
||||
</div>
|
||||
<div class="vv-children" id="vv-custom-children" style="display:none;">
|
||||
<?php if (empty($customs)): ?>
|
||||
<p class="vv-custom-empty">No custom scripts yet — click <strong>+ Add Script</strong> to create one.</p>
|
||||
<p class="vv-custom-empty">No custom scripts yet — click <strong>+ Create Script</strong> to create one.</p>
|
||||
<?php else: ?>
|
||||
|
||||
<?php // ── Folder groups ──────────────────────────────────────────────
|
||||
@@ -362,7 +362,8 @@ $runningScripts = array_unique($runningScripts);
|
||||
</div><!-- /#vv-sched-cards -->
|
||||
|
||||
<div class="vv-sched-footer">
|
||||
<button class="vv-save-btn vv-add-script-btn" onclick="vvAddScript()">+ Add Script</button>
|
||||
<button class="vv-save-btn vv-add-script-btn" onclick="vvAddScript()">+ Create Script</button>
|
||||
<button class="vv-save-btn vv-import-script-btn" onclick="vvImportScriptOpen()" title="Move an existing script from anywhere on the server into Custom Scripts">+ Import Script</button>
|
||||
<button class="vv-save-btn vv-new-folder-btn" onclick="vvNewFolder()" title="Create a folder in Custom Scripts">+ Folder</button>
|
||||
<button id="vv-arrange-btn" class="vv-save-btn" onclick="vvToggleArrange()" title="Drag scripts between orchestrators">Arrange</button>
|
||||
<button id="vv-arrange-save-btn" class="vv-save-btn vv-arrange-save-btn" onclick="vvSaveArrange()" style="display:none">Save Arrangement</button>
|
||||
@@ -1044,6 +1045,9 @@ function vvLH() {
|
||||
let vvSetupConf = <?= json_encode($vv_setup_conf) ?>;
|
||||
const vvLocalHostConf = <?= json_encode($_vv_local_host_conf) ?>;
|
||||
|
||||
// Where Custom Scripts (Create + Import) actually live — shown in the Import Script dialog
|
||||
window.__vvCustomScriptsDir = <?= json_encode(CUSTOM_SCRIPTS_DIR) ?>;
|
||||
|
||||
if (vvSetupConf) {
|
||||
// Auto-open the setup conf file once the page is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
@@ -1718,6 +1722,191 @@ function vvDeleteScript() {
|
||||
.catch(() => { btn.disabled = false; btn.textContent = '\u{1F5D1} Delete'; });
|
||||
}
|
||||
|
||||
// ── Import Script — browse the whole server, move a chosen .sh into Custom Scripts ──
|
||||
|
||||
let _vvImpSelected = null; // full path of the currently-selected file, or null
|
||||
|
||||
function vvImportScriptOpen() {
|
||||
let modal = document.getElementById('vv-import-modal');
|
||||
if (!modal) modal = _vvImpBuildModal();
|
||||
modal.style.display = 'flex';
|
||||
_vvImpSelected = null;
|
||||
_vvImpUpdateFooter();
|
||||
_vvImpBrowse('/');
|
||||
}
|
||||
|
||||
function vvImportScriptClose() {
|
||||
const modal = document.getElementById('vv-import-modal');
|
||||
if (modal) modal.style.display = 'none';
|
||||
_vvImpSelected = null;
|
||||
}
|
||||
|
||||
function _vvImpBuildModal() {
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'vv-import-modal';
|
||||
modal.style.cssText = 'display:none;position:fixed;inset:0;z-index:9000;background:rgba(0,0,0,.6);'
|
||||
+ 'align-items:center;justify-content:center;';
|
||||
modal.addEventListener('mousedown', e => { if (e.target === modal) vvImportScriptClose(); });
|
||||
|
||||
const box = document.createElement('div');
|
||||
box.style.cssText = 'background:#111;border:1px solid #222;border-radius:6px;width:560px;max-width:92vw;'
|
||||
+ 'max-height:80vh;display:flex;flex-direction:column;overflow:hidden;';
|
||||
|
||||
box.innerHTML = `
|
||||
<div style="padding:10px 14px;border-bottom:1px solid #1e1e1e;display:flex;align-items:center;justify-content:space-between;">
|
||||
<span style="font-size:13px;font-weight:bold;color:#ccc;">Import Script</span>
|
||||
<button onclick="vvImportScriptClose()" style="background:none;border:none;color:#666;cursor:pointer;font-size:14px;">✕</button>
|
||||
</div>
|
||||
<div style="padding:10px 14px;border-bottom:1px solid #1e1e1e;">
|
||||
<div style="font-size:10px;color:#555;margin-bottom:6px;">
|
||||
Moves the selected script into Custom Scripts (<code>${_vvImpEsc(window.__vvCustomScriptsDir || '')}</code>).
|
||||
The original is removed once the copy is verified.
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;align-items:center;">
|
||||
<input id="vv-imp-path" type="text" value="/" style="flex:1;min-width:0;background:#0a0a0a;border:1px solid #222;
|
||||
color:#aaa;border-radius:3px;padding:4px 8px;font-size:11px;font-family:monospace;"
|
||||
onkeydown="if(event.key==='Enter')_vvImpBrowse(document.getElementById('vv-imp-path').value.trim()||'/')">
|
||||
<button onclick="_vvImpNavUp()" title="Parent" style="background:#111;border:1px solid #222;color:#555;
|
||||
border-radius:3px;padding:4px 8px;cursor:pointer;font-size:12px;flex-shrink:0;">↑</button>
|
||||
<button onclick="_vvImpBrowse(document.getElementById('vv-imp-path').value.trim()||'/')" style="background:#111;
|
||||
border:1px solid #222;color:#4a9eff;border-radius:3px;padding:4px 9px;cursor:pointer;font-size:11px;
|
||||
white-space:nowrap;flex-shrink:0;">↻ Browse</button>
|
||||
</div>
|
||||
<span id="vv-imp-err" style="font-size:9px;color:#ef5350;display:none;margin-top:4px;"></span>
|
||||
</div>
|
||||
<div id="vv-imp-list" style="flex:1;overflow-y:auto;background:#080808;min-height:220px;"></div>
|
||||
<div style="padding:10px 14px;border-top:1px solid #1e1e1e;display:flex;align-items:center;justify-content:space-between;gap:10px;">
|
||||
<span id="vv-imp-selected" style="font-size:10px;color:#555;font-family:monospace;flex:1;min-width:0;
|
||||
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"></span>
|
||||
<div style="display:flex;gap:6px;flex-shrink:0;">
|
||||
<button onclick="vvImportScriptClose()" class="vv-btn-sm">Cancel</button>
|
||||
<button id="vv-imp-do-btn" class="vv-btn-sm vv-save-script-btn-style" disabled onclick="_vvImpDoImport()">Import</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
modal.appendChild(box);
|
||||
document.body.appendChild(modal);
|
||||
return modal;
|
||||
}
|
||||
|
||||
function _vvImpEsc(s) {
|
||||
const d = document.createElement('div');
|
||||
d.textContent = s;
|
||||
return d.innerHTML;
|
||||
}
|
||||
|
||||
function _vvImpBrowse(path) {
|
||||
const listEl = document.getElementById('vv-imp-list');
|
||||
const errEl = document.getElementById('vv-imp-err');
|
||||
const pathEl = document.getElementById('vv-imp-path');
|
||||
errEl.style.display = 'none';
|
||||
listEl.innerHTML = '<div style="padding:10px;color:#444;font-size:11px;">Loading…</div>';
|
||||
|
||||
fetch(`/plugins/varaverk/api/import_script.php?action=browse&path=${encodeURIComponent(path)}&_=${Date.now()}`)
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (!d.ok) {
|
||||
listEl.innerHTML = '';
|
||||
errEl.textContent = d.error || 'Browse failed';
|
||||
errEl.style.display = '';
|
||||
return;
|
||||
}
|
||||
pathEl.value = d.path;
|
||||
_vvImpRender(d);
|
||||
})
|
||||
.catch(e => {
|
||||
listEl.innerHTML = '';
|
||||
errEl.textContent = 'Request failed: ' + e;
|
||||
errEl.style.display = '';
|
||||
});
|
||||
}
|
||||
|
||||
function _vvImpNavUp() {
|
||||
const cur = (document.getElementById('vv-imp-path').value || '/').replace(/\/$/, '') || '/';
|
||||
const up = cur === '/' ? '/' : (cur.substring(0, cur.lastIndexOf('/')) || '/');
|
||||
_vvImpBrowse(up);
|
||||
}
|
||||
|
||||
function _vvImpRender(d) {
|
||||
const listEl = document.getElementById('vv-imp-list');
|
||||
let html = '';
|
||||
|
||||
if (d.parent !== null) {
|
||||
const pname = d.parent === '/' ? '/' : (d.parent.replace(/^.*\//, '') || d.parent) + '/';
|
||||
html += `<div class="vv-imp-row" onclick="_vvImpBrowse(${JSON.stringify(d.parent)})"
|
||||
style="padding:5px 12px;font-size:11px;color:#444;font-style:italic;cursor:pointer;font-family:monospace;">↑ ${pname}</div>`;
|
||||
}
|
||||
|
||||
for (const dir of d.dirs) {
|
||||
const name = dir.replace(/^.*\//, '') || dir;
|
||||
html += `<div class="vv-imp-row" onclick="_vvImpBrowse(${JSON.stringify(dir)})" title="${_vvImpEsc(dir)}"
|
||||
style="padding:5px 12px;font-size:11px;color:#666;cursor:pointer;font-family:monospace;">▶ ${_vvImpEsc(name)}</div>`;
|
||||
}
|
||||
|
||||
for (const file of d.files) {
|
||||
const name = file.replace(/^.*\//, '') || file;
|
||||
const sel = file === _vvImpSelected;
|
||||
html += `<div class="vv-imp-row vv-imp-file${sel ? ' vv-imp-file-sel' : ''}" data-path="${_vvImpEsc(file)}"
|
||||
onclick="_vvImpSelectFile(${JSON.stringify(file)})" title="${_vvImpEsc(file)}"
|
||||
style="padding:5px 12px;font-size:11px;cursor:pointer;font-family:monospace;
|
||||
color:${sel ? '#4caf50' : '#4a9eff'};background:${sel ? '#0f1f0f' : 'transparent'};">📄 ${_vvImpEsc(name)}</div>`;
|
||||
}
|
||||
|
||||
if (!d.dirs.length && !d.files.length) {
|
||||
html += '<div style="padding:10px 12px;color:#333;font-size:11px;">— empty —</div>';
|
||||
}
|
||||
|
||||
listEl.innerHTML = html;
|
||||
}
|
||||
|
||||
function _vvImpSelectFile(path) {
|
||||
_vvImpSelected = path;
|
||||
_vvImpUpdateFooter();
|
||||
// Re-render just the highlight without a re-fetch.
|
||||
document.querySelectorAll('#vv-imp-list .vv-imp-file').forEach(el => {
|
||||
const isSel = el.dataset.path === path;
|
||||
el.classList.toggle('vv-imp-file-sel', isSel);
|
||||
el.style.color = isSel ? '#4caf50' : '#4a9eff';
|
||||
el.style.background = isSel ? '#0f1f0f' : 'transparent';
|
||||
});
|
||||
}
|
||||
|
||||
function _vvImpUpdateFooter() {
|
||||
const sel = document.getElementById('vv-imp-selected');
|
||||
const btn = document.getElementById('vv-imp-do-btn');
|
||||
if (!sel || !btn) return;
|
||||
sel.textContent = _vvImpSelected || '';
|
||||
btn.disabled = !_vvImpSelected;
|
||||
}
|
||||
|
||||
function _vvImpDoImport() {
|
||||
if (!_vvImpSelected) return;
|
||||
const dest = (window.__vvCustomScriptsDir || 'Custom Scripts') + '/' + _vvImpSelected.replace(/^.*\//, '');
|
||||
if (!confirm('Move\n ' + _vvImpSelected + '\n→ ' + dest + '\n\nThe original will be deleted once the copy is verified. Continue?')) return;
|
||||
|
||||
const btn = document.getElementById('vv-imp-do-btn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Importing…';
|
||||
|
||||
vvPost('/plugins/varaverk/api/import_script.php', {action: 'import', path: _vvImpSelected})
|
||||
.then(d => {
|
||||
if (!d.ok) {
|
||||
alert('Import failed: ' + (d.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Import';
|
||||
return;
|
||||
}
|
||||
if (d.warning) alert(d.warning);
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(e => {
|
||||
alert('Import failed: ' + e);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Import';
|
||||
});
|
||||
}
|
||||
|
||||
function vvShowConfMode(title) {
|
||||
document.getElementById('vv-suggestions').style.display = 'none';
|
||||
document.getElementById('vv-log-pre').style.display = 'none';
|
||||
|
||||
Reference in New Issue
Block a user