ShareLinks: resolve movie id from home cards and tighten guest lockdown

Creating a link from a home-page carousel resolved the wrong id: the
card context menu detaches into a body-level action sheet, so the DOM
walk missed the movie and fell back to the first data-id on the page (a
library folder), which then got rejected. Capture the pointed-at card's
item id at pointerdown so the action injects the right id.

Guest lockdown let guests reach the home page: nav was hidden by
matching English label keywords, which never matched the French UI, and
any details/list route counted as allowed. Hide the header
back/home/drawer/search buttons by CSS class instead, and treat only the
guest's own item page (or a playback route) as allowed so off-item
routes redirect back to the shared title.
Cette révision appartient à :
Franciskid
2026-07-06 23:49:01 +02:00
Parent c964b4e37a
révision e560c65d5a
+36 -5
Voir le fichier
@@ -2,7 +2,7 @@
var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6'; var pluginId = '68540b76-ee74-436d-85ff-2abc884bbea6';
var copyLabel = 'Copy Stream URL'; var copyLabel = 'Copy Stream URL';
var actionLabel = 'Create guest link'; var actionLabel = 'Create guest link';
var clientVersion = '1.0.0-ui-modal-6'; var clientVersion = '1.0.0-ui-modal-7';
var allowedItemStorageKey = 'sharelinks.allowedItemId'; var allowedItemStorageKey = 'sharelinks.allowedItemId';
var guestClassName = 'sharelinks-guest'; var guestClassName = 'sharelinks-guest';
var hiddenAttr = 'data-sharelinks-hidden'; var hiddenAttr = 'data-sharelinks-hidden';
@@ -14,6 +14,8 @@
var scanQueued = false; var scanQueued = false;
var bootRetry = null; var bootRetry = null;
var observer = null; var observer = null;
var lastContextItemId = null;
var lastContextItemTs = 0;
var historyPatched = false; var historyPatched = false;
var durationOptions = [ var durationOptions = [
{ label: '1 hour', hours: 1 }, { label: '1 hour', hours: 1 },
@@ -62,6 +64,19 @@
window.addEventListener('hashchange', scheduleWork, true); window.addEventListener('hashchange', scheduleWork, true);
window.addEventListener('popstate', scheduleWork, true); window.addEventListener('popstate', scheduleWork, true);
document.addEventListener('pointerdown', function (event) {
var node = event.target;
while (node && node !== document) {
var id = readItemIdFromNode(node);
if (id) {
lastContextItemId = id;
lastContextItemTs = Date.now();
return;
}
node = node.parentElement;
}
}, true);
observer = new MutationObserver(scheduleWork); observer = new MutationObserver(scheduleWork);
observer.observe(document.body, { childList: true, subtree: true }); observer.observe(document.body, { childList: true, subtree: true });
@@ -162,7 +177,7 @@
// UX-only lockdown: the guest user's real access boundary is still the // UX-only lockdown: the guest user's real access boundary is still the
// server-side policy and item tags. This just keeps the web client out // server-side policy and item tags. This just keeps the web client out
// of the user's way. // of the user's way.
if (context.allowedItemId && !isAllowedLocation()) { if (context.allowedItemId && !isAllowedLocation(context.allowedItemId)) {
navigateToItem(context.allowedItemId); navigateToItem(context.allowedItemId);
} }
} }
@@ -211,7 +226,11 @@
var style = document.createElement('style'); var style = document.createElement('style');
style.id = 'ShareLinksGuestStyle'; style.id = 'ShareLinksGuestStyle';
style.textContent = 'body.' + guestClassName + ' [' + hiddenAttr + '="1"] { display: none !important; }'; style.textContent = 'body.' + guestClassName + ' [' + hiddenAttr + '="1"],'
+ ' body.' + guestClassName + ' .headerBackButton,'
+ ' body.' + guestClassName + ' .headerHomeButton,'
+ ' body.' + guestClassName + ' .mainDrawerButton,'
+ ' body.' + guestClassName + ' .headerSearchButton { display: none !important; }';
document.head.appendChild(style); document.head.appendChild(style);
} }
@@ -425,6 +444,10 @@
current = current.parentElement; current = current.parentElement;
} }
if (lastContextItemId && (Date.now() - lastContextItemTs) < 8000) {
return lastContextItemId;
}
return findItemIdInDocument(); return findItemIdInDocument();
} }
@@ -505,8 +528,16 @@
return /#\/(?:details|video|playback|list|item)/i.test(location.hash || ''); return /#\/(?:details|video|playback|list|item)/i.test(location.hash || '');
} }
function isAllowedLocation() { function isAllowedLocation(allowedItemId) {
return isDetailsOrPlaybackRoute(); var hash = location.hash || '';
if (/#\/(?:video|playback)/i.test(hash)) {
return true;
}
if (/#\/(?:details|item)/i.test(hash)) {
var id = parseItemIdFromUrl();
return !!id && !!allowedItemId && id.toLowerCase() === String(allowedItemId).toLowerCase();
}
return false;
} }
function navigateToItem(itemId) { function navigateToItem(itemId) {