Skip to content

Commit d73221e

Browse files
Fix regressions from full plugin bug review pass
1 parent 2786f2b commit d73221e

9 files changed

Lines changed: 127 additions & 53 deletions

File tree

209 KB
Binary file not shown.

folderview.plus.plg

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/folderview.plus.plg">
9-
<!ENTITY version "2026.03.08.8">
10-
<!ENTITY md5 "8d2498345e54795abf0112335c888025">
9+
<!ENTITY version "2026.03.08.9">
10+
<!ENTITY md5 "67099f6404923957ab251e0a36316997">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-open-o" support="https://github.com/alexphillips-dev/FolderView-Plus/issues" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.03.08.9
17+
- Full bugfix pass for regressions and stability:
18+
- scope custom-action dialog styling to its own dialog widget (no global `.ui-dialog` side effects),
19+
- fix delayed refresh calls by passing `loadlist` to `setTimeout` instead of invoking it immediately,
20+
- harden Docker folder row centering logic to detect folder rows/cells even when clone markup differs,
21+
- add input guards and JSON error responses for create/update/delete server endpoints.
22+
1623
###2026.03.08.8
1724
- Fix import preview dialog UI polish:
1825
- stabilize import-selection scroll position while toggling items,

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@ const rmDockerFolder = (id) => {
829829
$('div.spinner.fixed').show('slow');
830830
await $.get('/plugins/folderview.plus/server/delete.php?type=docker&id=' + id).promise();
831831
loadedFolder = false;
832-
setTimeout(loadlist(), 500)
832+
setTimeout(loadlist, 500)
833833
});
834834
};
835835

@@ -854,7 +854,7 @@ const rmVMFolder = (id) => {
854854
$('div.spinner.fixed').show('slow');
855855
await $.get('/plugins/folderview.plus/server/delete.php?type=vm&id=' + id).promise();
856856
loadedFolder = false;
857-
setTimeout(loadlist(), 500)
857+
setTimeout(loadlist, 500)
858858
});
859859
};
860860

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,32 @@ const escapeClassToken = (value) => {
6464
return input.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
6565
};
6666

67+
const getFolderNameCell = (row) => {
68+
if (!row || !row.querySelector) {
69+
return null;
70+
}
71+
const direct = row.querySelector('td.ct-name.folder-name');
72+
if (direct) {
73+
return direct;
74+
}
75+
const sub = row.querySelector('.folder-name-sub');
76+
return sub && sub.closest ? sub.closest('td') : null;
77+
};
78+
79+
const getFolderRows = () => {
80+
const rows = [];
81+
const seen = new Set();
82+
document.querySelectorAll('tr').forEach((row) => {
83+
const cell = getFolderNameCell(row);
84+
if (!cell || seen.has(row)) {
85+
return;
86+
}
87+
seen.add(row);
88+
rows.push(row);
89+
});
90+
return rows;
91+
};
92+
6793
const getFolderIdFromRow = (row) => {
6894
if (!row) {
6995
return '';
@@ -75,7 +101,7 @@ const getFolderIdFromRow = (row) => {
75101
}
76102
}
77103
}
78-
const label = row.querySelector && row.querySelector('td.ct-name.folder-name span.appname a');
104+
const label = row.querySelector && row.querySelector('span.appname a');
79105
if (label && typeof label.textContent === 'string') {
80106
const text = label.textContent.trim();
81107
if (text.startsWith('folder-') && text.length > 7) {
@@ -89,7 +115,7 @@ const getFolderNameFromRow = (row) => {
89115
if (!row || !row.querySelector) {
90116
return '';
91117
}
92-
const label = row.querySelector('td.ct-name.folder-name a.exec.folder-appname');
118+
const label = row.querySelector('a.exec.folder-appname');
93119
if (!label || typeof label.textContent !== 'string') {
94120
return '';
95121
}
@@ -132,16 +158,16 @@ const applyRowHeight = (row, height = 0) => {
132158
});
133159
};
134160

135-
const buildMainFolderHeightLookup = () => {
161+
const buildMainFolderHeightLookup = (mainRows = []) => {
136162
const byId = new Map();
137163
const byName = new Map();
138164
const ordered = [];
139165

140-
const mainRows = Array.from(document.querySelectorAll('tr')).filter((row) => {
141-
return !!(row && rowHasFolderPreview(row) && row.querySelector && row.querySelector('td.ct-name.folder-name'));
142-
});
166+
const rows = Array.isArray(mainRows) && mainRows.length
167+
? mainRows
168+
: getFolderRows().filter((row) => rowHasFolderPreview(row));
143169

144-
mainRows.forEach((row) => {
170+
rows.forEach((row) => {
145171
const height = getRenderedRowHeight(row);
146172
if (height <= 0) {
147173
return;
@@ -213,18 +239,10 @@ const applyFolderCellCentering = (cell, rowHeight = 0) => {
213239
};
214240

215241
const forceAllFolderRowsVerticalCenter = () => {
216-
const lookup = buildMainFolderHeightLookup();
217-
218-
const cloneRows = [];
219-
const cloneSeen = new Set();
220-
document.querySelectorAll('td.ct-name.folder-name').forEach((cell) => {
221-
const row = cell.parentElement;
222-
if (!row || rowHasFolderPreview(row) || cloneSeen.has(row)) {
223-
return;
224-
}
225-
cloneSeen.add(row);
226-
cloneRows.push(row);
227-
});
242+
const rows = getFolderRows();
243+
const sourceRows = rows.filter((row) => rowHasFolderPreview(row));
244+
const cloneRows = rows.filter((row) => !rowHasFolderPreview(row));
245+
const lookup = buildMainFolderHeightLookup(sourceRows);
228246

229247
cloneRows.forEach((row, index) => {
230248
const folderId = getFolderIdFromRow(row);
@@ -240,23 +258,16 @@ const forceAllFolderRowsVerticalCenter = () => {
240258
}
241259

242260
applyRowHeight(row, targetHeight);
243-
row.querySelectorAll('td.ct-name.folder-name').forEach((cell) => {
261+
const cell = getFolderNameCell(row);
262+
if (cell) {
244263
applyFolderCellCentering(cell, targetHeight);
245-
});
264+
}
246265
});
247266

248-
document.querySelectorAll('tr').forEach((row) => {
249-
if (!rowHasFolderPreview(row)) {
250-
return;
251-
}
252-
row.querySelectorAll('td.ct-name.folder-name').forEach((cell) => {
253-
applyFolderCellCentering(cell, 0);
254-
});
267+
sourceRows.forEach((row) => {
255268
applyRowHeight(row, 0);
256-
});
257-
document.querySelectorAll('tr td.ct-name.folder-name').forEach((cell) => {
258-
const row = cell.parentElement;
259-
if (row && !rowHasFolderPreview(row) && !cloneSeen.has(row)) {
269+
const cell = getFolderNameCell(row);
270+
if (cell) {
260271
applyFolderCellCentering(cell, 0);
261272
}
262273
});
@@ -299,8 +310,8 @@ const startFolderRowCenterObserver = () => {
299310
}
300311
const element = node;
301312
if (
302-
(element.matches && element.matches('td.ct-name.folder-name, tr[class*="folder-id-"]'))
303-
|| (element.querySelector && element.querySelector('td.ct-name.folder-name, tr[class*="folder-id-"]'))
313+
(element.matches && element.matches('td.ct-name.folder-name, tr[class*="folder-id-"], .folder-name-sub, .folder-appname'))
314+
|| (element.querySelector && element.querySelector('td.ct-name.folder-name, tr[class*="folder-id-"], .folder-name-sub, .folder-appname'))
304315
) {
305316
queueForceAllFolderRowsVerticalCenter();
306317
return;

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,11 +1122,12 @@ const customAction = (action = undefined) => {
11221122
dialog.find('[name="action_elements"]').multiselect("destroy");
11231123
}
11241124
});
1125-
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
1126-
$('.ui-dialog .ui-dialog-titlebar-close').css({'display':'none'});
1127-
$(".ui-dialog .ui-dialog-title").css({'text-align':'center','width':'100%'});
1128-
$(".ui-dialog .ui-dialog-content").css({'padding-top':'15px','vertical-align':'bottom'});
1129-
$(".ui-button-text").css({'padding':'0px 5px'});
1125+
const dialogWidget = dialog.closest('.ui-dialog');
1126+
dialogWidget.find('.ui-dialog-titlebar').addClass('menu');
1127+
dialogWidget.find('.ui-dialog-titlebar-close').css({ display: 'none' });
1128+
dialogWidget.find('.ui-dialog-title').css({ 'text-align': 'center', width: '100%' });
1129+
dialogWidget.find('.ui-dialog-content').css({ 'padding-top': '15px', 'vertical-align': 'bottom' });
1130+
dialogWidget.find('.ui-button-text').css({ padding: '0px 5px' });
11301131
return false;
11311132
};
11321133

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/vm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ const rmFolder = (id) => {
570570
$('div.spinner.fixed').show('slow');
571571
await $.get('/plugins/folderview.plus/server/delete.php?type=vm&id=' + id).promise();
572572
loadedFolder = false;
573-
setTimeout(loadlist(), 500)
573+
setTimeout(loadlist, 500)
574574
});
575575
};
576576

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
<?php
2-
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3-
updateFolder($_POST['type'], $_POST['content']);
4-
?>
2+
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3+
4+
header('Content-Type: application/json');
5+
6+
try {
7+
$type = (string)($_REQUEST['type'] ?? '');
8+
$content = (string)($_REQUEST['content'] ?? '');
9+
if ($type === '' || $content === '') {
10+
throw new RuntimeException('Missing required parameters.');
11+
}
12+
13+
updateFolder($type, $content);
14+
echo json_encode(['ok' => true]);
15+
} catch (Throwable $e) {
16+
http_response_code(400);
17+
echo json_encode([
18+
'ok' => false,
19+
'error' => $e->getMessage()
20+
]);
21+
}
22+
?>
Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,22 @@
11
<?php
2-
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3-
deleteFolder($_GET['type'], $_GET['id']);
4-
?>
2+
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3+
4+
header('Content-Type: application/json');
5+
6+
try {
7+
$type = (string)($_REQUEST['type'] ?? '');
8+
$id = (string)($_REQUEST['id'] ?? '');
9+
if ($type === '' || $id === '') {
10+
throw new RuntimeException('Missing required parameters.');
11+
}
12+
13+
deleteFolder($type, $id);
14+
echo json_encode(['ok' => true]);
15+
} catch (Throwable $e) {
16+
http_response_code(400);
17+
echo json_encode([
18+
'ok' => false,
19+
'error' => $e->getMessage()
20+
]);
21+
}
22+
?>
Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
<?php
2-
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3-
updateFolder($_POST['type'], $_POST['content'], $_POST['id']);
4-
?>
2+
require_once("/usr/local/emhttp/plugins/folderview.plus/server/lib.php");
3+
4+
header('Content-Type: application/json');
5+
6+
try {
7+
$type = (string)($_REQUEST['type'] ?? '');
8+
$content = (string)($_REQUEST['content'] ?? '');
9+
$id = (string)($_REQUEST['id'] ?? '');
10+
if ($type === '' || $content === '' || $id === '') {
11+
throw new RuntimeException('Missing required parameters.');
12+
}
13+
14+
updateFolder($type, $content, $id);
15+
echo json_encode(['ok' => true]);
16+
} catch (Throwable $e) {
17+
http_response_code(400);
18+
echo json_encode([
19+
'ok' => false,
20+
'error' => $e->getMessage()
21+
]);
22+
}
23+
?>

0 commit comments

Comments
 (0)