Sometimes, using PnP PowerShell just isn’t an option. Whether it’s because of restrictions in your environment or you simply need a quick peek at your SharePoint data, you need another way. I often find myself needing a fast report on lists, fields, subsites, etc.
Enter JavaScript! I know, it might sound a bit unconventional, but running JavaScript right in the browser console has come to the rescue more than once. This method gives me a quick report on the fly. No extra tools required. It’s not the most elegant approach out there, but when you need to discover your SharePoint data fast, it really gets the job done.
Below is a small collection of scripts that does the heavy lifting for you. It starts at your current SharePoint site, grabs all the subsites (webs) recursively, and then pulls in every list along with its content types. Once it’s done, it shows you a neat table right in your browser. You can even downloads a CSV report. Super handy, don’t you agree? 😃
(async function() {// Resolve the SharePoint context — works on classic and modern pagesasync function getSPContext() {const ctxAvailable = typeof _spPageContextInfo !== 'undefined' && _spPageContextInfo.webAbsoluteUrl;let url = ctxAvailable ? _spPageContextInfo.webAbsoluteUrl : window.location.href.split('?')[0].split('#')[0];if (!ctxAvailable) {for (const s of ['/_layouts/','/_api/','/SitePages/','/Lists/','/Forms/','/Pages/','/SiteAssets/','/_app/']) {const i = url.toLowerCase().indexOf(s.toLowerCase());if (i > -1) { url = url.substring(0, i); break; }}url = url.replace(/\/[^\/]*\.aspx$/i, '');}const r = await fetch(`${url}/_api/contextinfo`, { method:'POST', headers:{'Accept':'application/json;odata=verbose'}, credentials:'same-origin' });const c = (await r.json()).d.GetContextWebInformation;return { webUrl: c.WebFullUrl, siteUrl: c.SiteFullUrl, formDigest: c.FormDigestValue };}const ctx = await getSPContext();const siteUrl = ctx.webUrl;// Create UI elementsconst container = document.createElement('div');container.style.cssText = 'position:fixed;top:20px;right:20px;width:98%;max-width:1600px;background:white;border:2px solid #0078d4;padding:20px;z-index:10000;max-height:80vh;overflow-y:auto;box-shadow:0 4px 6px rgba(0,0,0,0.1);user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;';const controls = document.createElement('div');controls.style.cssText = 'margin-bottom:20px;display:flex;align-items:center;';const downloadButton = document.createElement('button');downloadButton.textContent = 'Download CSV';downloadButton.style.cssText = 'padding:8px 16px;margin-right:10px;background:#107c10;color:white;border:none;cursor:pointer;display:none;';const infoText = document.createElement('span');infoText.style.cssText = 'margin-right:auto;font-weight:bold;color:#107c10;';const closeButton = document.createElement('button');closeButton.textContent = 'Close';closeButton.style.cssText = 'padding:8px 16px;background:#d83b01;color:white;border:none;cursor:pointer;margin-left:20px;';const tableContainer = document.createElement('div');controls.appendChild(downloadButton);controls.appendChild(infoText);controls.appendChild(closeButton);container.appendChild(controls);container.appendChild(tableContainer);document.body.appendChild(container);let allContentTypesData = [];let rootWebUrl = '';// Recursively fetch all subsites (webs)async function fetchWebAndSubsites(webUrl) {const allWebs = [];try {// Get current web infoconst webResponse = await fetch(`${webUrl}/_api/web?$select=Title,Url,ServerRelativeUrl`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (!webResponse.ok) {throw new Error('Failed to fetch web info for: ' + webUrl);}const webData = await webResponse.json();const currentWeb = webData.d;allWebs.push({Title: currentWeb.Title,Url: currentWeb.Url,ServerRelativeUrl: currentWeb.ServerRelativeUrl});// Get child websconst subWebsResponse = await fetch(`${webUrl}/_api/web/webs?$select=Title,Url,ServerRelativeUrl`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (subWebsResponse.ok) {const subWebsData = await subWebsResponse.json();const subWebs = subWebsData.d.results;// Recursively get all child websfor (const subWeb of subWebs) {const childWebs = await fetchWebAndSubsites(subWeb.Url);allWebs.push(...childWebs);}}} catch (error) {console.error(`Error fetching web ${webUrl}:`, error);}return allWebs;}// Fetch lists for a given webasync function fetchWebLists(webUrl) {try {const response = await fetch(`${webUrl}/_api/web/lists?$select=Title,Id,BaseTemplate,DefaultViewUrl&$filter=Hidden eq false`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (!response.ok) {throw new Error('Failed to fetch lists for: ' + webUrl);}const data = await response.json();return data.d.results;} catch (error) {console.error(`Error fetching lists for ${webUrl}:`, error);return [];}}// Load all dataconst loadData = async () => {try {tableContainer.innerHTML = '<div style="color:#0078d4;font-style:italic;">Loading all sites and their lists with content types...</div>';allContentTypesData = [];// Use the resolved web URL from getSPContext()rootWebUrl = siteUrl;// Get all webs recursivelyinfoText.textContent = 'Fetching all sites...';const allWebs = await fetchWebAndSubsites(rootWebUrl);// For each web, get its lists and content typeslet totalSites = allWebs.length;let processedSites = 0;for (const web of allWebs) {processedSites++;infoText.textContent = `Processing site ${processedSites} of ${totalSites}: ${web.Title}`;const lists = await fetchWebLists(web.Url);// For each list, get content typesfor (const list of lists) {try {const ctResponse = await fetch(`${web.Url}/_api/web/lists/getbytitle('${encodeURIComponent(list.Title)}')/contenttypes?$select=Name,Id,Description,Group,Hidden,ReadOnly`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (ctResponse.ok) {const ctData = await ctResponse.json();const contentTypes = ctData.d.results;// Create a row for each content typecontentTypes.forEach(ct => {allContentTypesData.push({SiteTitle: web.Title,SiteUrl: web.Url,SiteRelativeUrl: web.ServerRelativeUrl,ListTitle: list.Title,ListUrl: list.DefaultViewUrl,ContentTypeName: ct.Name,ContentTypeId: ct.Id.StringValue,Description: ct.Description || '',Group: ct.Group || '',Hidden: ct.Hidden ? 'Yes' : 'No',ReadOnly: ct.ReadOnly ? 'Yes' : 'No'});});}} catch (error) {console.error(`Error loading content types for ${web.Title}/${list.Title}:`, error);}}}// Display infoconst totalCTs = allContentTypesData.length;const uniqueLists = new Set(allContentTypesData.map(item => `${item.SiteUrl}|${item.ListTitle}`)).size;infoText.textContent = `Total sites: ${totalSites}, Total lists: ${uniqueLists}, Total content types: ${totalCTs}`;// Create tablelet tableHTML = `<table style="width:100%;border-collapse:collapse;margin-top:10px;user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;"><thead><tr style="background:#0078d4;color:white;"><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Site Title</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Site URL</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">List Title</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">List URL</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Content Type Name</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;width:180px;max-width:180px;user-select:text;">Content Type ID</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;width:150px;user-select:text;">Description</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Group</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Hidden</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;user-select:text;">Read Only</th></tr></thead><tbody>`;let currentSite = '';let currentList = '';allContentTypesData.forEach((row, index) => {const bgColor = index % 2 === 0 ? '#f2f2f2' : 'white';const siteChanged = row.SiteUrl !== currentSite;const listChanged = siteChanged || `${row.SiteUrl}|${row.ListTitle}` !== currentList;const siteStyle = siteChanged ? 'font-weight:bold;border-top:3px solid #0078d4;' : '';const listStyle = listChanged ? 'font-weight:bold;border-top:2px solid #0078d4;' : '';currentSite = row.SiteUrl;currentList = `${row.SiteUrl}|${row.ListTitle}`;// Create clickable URLsconst listUrlHtml = row.ListUrl ?`<a href="${row.SiteUrl}${row.ListUrl}" target="_blank" style="color:#0078d4;text-decoration:none;">Open</a>` :'';tableHTML += `<tr style="background:${bgColor};"><td style="border:1px solid #ddd;padding:8px;${siteStyle}user-select:text;cursor:text;">${row.SiteTitle}</td><td style="border:1px solid #ddd;padding:8px;${siteStyle}user-select:text;cursor:text;font-size:12px;">${row.SiteRelativeUrl}</td><td style="border:1px solid #ddd;padding:8px;${listStyle}user-select:text;cursor:text;">${row.ListTitle}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;user-select:text;">${listUrlHtml}</td><td style="border:1px solid #ddd;padding:8px;user-select:text;cursor:text;">${row.ContentTypeName}</td><td style="border:1px solid #ddd;padding:8px;font-size:11px;word-break:break-all;width:180px;max-width:180px;user-select:text;cursor:text;">${row.ContentTypeId}</td><td style="border:1px solid #ddd;padding:8px;user-select:text;cursor:text;font-size:12px;width:150px;overflow:hidden;text-overflow:ellipsis;" title="${row.Description.replace(/"/g, '"')}">${row.Description}</td><td style="border:1px solid #ddd;padding:8px;user-select:text;cursor:text;">${row.Group}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;user-select:text;cursor:text;">${row.Hidden}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;user-select:text;cursor:text;">${row.ReadOnly}</td></tr>`;});tableHTML += '</tbody></table>';tableContainer.innerHTML = tableHTML;// Show download buttondownloadButton.style.display = 'inline-block';// Also log to consoleconsole.table(allContentTypesData);} catch (error) {console.error('Error loading data:', error);tableContainer.innerHTML = `<div style="color:red;">Error: ${error.message}</div>`;}};// Download CSVdownloadButton.onclick = () => {if (!allContentTypesData.length) return;// Create CSV contentlet csv = 'Site Title,Site URL,List Title,List URL,Content Type Name,Content Type ID,Description,Group,Hidden,Read Only\n';allContentTypesData.forEach(row => {const fullListUrl = row.ListUrl ? `${row.SiteUrl}${row.ListUrl}` : '';csv += `"${(row.SiteTitle || '').replace(/"/g, '""')}","${(row.SiteUrl || '').replace(/"/g, '""')}","${(row.ListTitle || '').replace(/"/g, '""')}","${fullListUrl.replace(/"/g, '""')}","${(row.ContentTypeName || '').replace(/"/g, '""')}","${(row.ContentTypeId || '').replace(/"/g, '""')}","${(row.Description || '').replace(/"/g, '""')}","${(row.Group || '').replace(/"/g, '""')}","${row.Hidden}","${row.ReadOnly}"\n`;});// Downloadconst blob = new Blob([csv], { type: 'text/csv' });const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = `all_sites_lists_content_types_${new Date().toISOString().split('T')[0]}.csv`;document.body.appendChild(a);a.click();document.body.removeChild(a);window.URL.revokeObjectURL(url);};// Close buttoncloseButton.onclick = () => {document.body.removeChild(container);};// Auto-load on startloadData();})();
Results:
(async function() {// Resolve the SharePoint context — works on classic and modern pagesasync function getSPContext() {const ctxAvailable = typeof _spPageContextInfo !== 'undefined' && _spPageContextInfo.webAbsoluteUrl;let url = ctxAvailable ? _spPageContextInfo.webAbsoluteUrl : window.location.href.split('?')[0].split('#')[0];if (!ctxAvailable) {for (const s of ['/_layouts/','/_api/','/SitePages/','/Lists/','/Forms/','/Pages/','/SiteAssets/','/_app/']) {const i = url.toLowerCase().indexOf(s.toLowerCase());if (i > -1) { url = url.substring(0, i); break; }}url = url.replace(/\/[^\/]*\.aspx$/i, '');}const r = await fetch(`${url}/_api/contextinfo`, { method:'POST', headers:{'Accept':'application/json;odata=verbose'}, credentials:'same-origin' });const c = (await r.json()).d.GetContextWebInformation;return { webUrl: c.WebFullUrl, siteUrl: c.SiteFullUrl, formDigest: c.FormDigestValue };}// Get current site URL - will be determined from contextlet siteUrl = '';// Create floating button that persistsconst floatingButton = document.createElement('button');floatingButton.innerHTML = '📋 Lists';floatingButton.style.cssText = 'position:fixed;bottom:20px;right:20px;padding:12px 20px;background:#0078d4;color:white;border:none;border-radius:25px;cursor:pointer;z-index:9999;box-shadow:0 4px 6px rgba(0,0,0,0.2);font-size:14px;font-weight:bold;display:none;';floatingButton.onmouseover = function() { this.style.background = '#106ebe'; };floatingButton.onmouseout = function() { this.style.background = '#0078d4'; };document.body.appendChild(floatingButton);// Create UI elementsconst container = document.createElement('div');container.style.cssText = 'position:fixed;top:20px;right:20px;width:95%;max-width:1500px;background:white;border:2px solid #0078d4;padding:20px;z-index:10000;max-height:80vh;overflow-y:auto;box-shadow:0 4px 6px rgba(0,0,0,0.1);user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;';const controls = document.createElement('div');controls.style.cssText = 'margin-bottom:20px;display:flex;align-items:center;';// Filter inputconst filterInput = document.createElement('input');filterInput.type = 'text';filterInput.placeholder = 'Filter lists by title...';filterInput.style.cssText = 'padding:8px;margin-right:10px;border:1px solid #ccc;border-radius:4px;width:200px;';// Toggle hidden lists buttonconst toggleHiddenButton = document.createElement('button');toggleHiddenButton.textContent = '👁️ Show System Lists';toggleHiddenButton.style.cssText = 'padding:8px 16px;margin-right:10px;background:#6c757d;color:white;border:none;cursor:pointer;';const downloadButton = document.createElement('button');downloadButton.textContent = 'Download CSV';downloadButton.style.cssText = 'padding:8px 16px;margin-right:10px;background:#107c10;color:white;border:none;cursor:pointer;display:none;';const infoText = document.createElement('span');infoText.style.cssText = 'margin-right:auto;font-weight:bold;color:#107c10;';const closeButton = document.createElement('button');closeButton.textContent = 'Close';closeButton.style.cssText = 'padding:8px 16px;background:#d83b01;color:white;border:none;cursor:pointer;margin-left:20px;';const contentContainer = document.createElement('div');controls.appendChild(filterInput);controls.appendChild(toggleHiddenButton);controls.appendChild(downloadButton);controls.appendChild(infoText);controls.appendChild(closeButton);container.appendChild(controls);container.appendChild(contentContainer);document.body.appendChild(container);let allListsData = [];let selectedListId = null;let showHiddenLists = false;// Create modal for list detailsfunction createDetailsModal(list) {// Create overlayconst overlay = document.createElement('div');overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.3);z-index:10000;';const modal = document.createElement('div');modal.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90%;max-width:800px;background:white;border:2px solid #0078d4;z-index:10001;box-shadow:0 8px 16px rgba(0,0,0,0.2);max-height:80vh;overflow:hidden;';const modalContent = `<div style="background:#0078d4;padding:10px 20px;cursor:move;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;" id="modalHeader"><h2 style="margin:0;color:white;">${list.Title}</h2></div><div style="padding:20px;overflow-y:auto;max-height:calc(80vh - 60px);"><div style="display:grid;grid-template-columns:150px 1fr;gap:10px;color:#333;"><strong style="color:#333;">List GUID:</strong><span style="user-select:text;font-family:monospace;color:#333;">${list.Id}</span><strong style="color:#333;">Title:</strong><span style="color:#333;">${list.Title}</span><strong style="color:#333;">Description:</strong><span style="color:#333;">${list.Description || 'No description'}</span><strong style="color:#333;">Item Count:</strong><span style="color:#333;">${list.ItemCount.toLocaleString()}</span><strong style="color:#333;">List URL:</strong><span style="word-break:break-all;color:#333;"><a href="${list.ListUrl}" target="_blank" style="color:#0078d4;">${list.ListUrl}</a></span><strong style="color:#333;">Default View URL:</strong><span style="word-break:break-all;color:#333;"><a href="${list.DefaultViewUrl}" target="_blank" style="color:#0078d4;">${list.DefaultViewUrl}</a></span><strong style="color:#333;">Root Folder:</strong><span style="color:#333;">${list.RootFolder}</span><strong style="color:#333;">Entity Type:</strong><span style="color:#333;">${list.EntityTypeName}</span><strong style="color:#333;">Base Template:</strong><span style="color:#333;">${list.BaseTemplate} (${list.BaseType})</span><strong style="color:#333;">Created:</strong><span style="color:#333;">${list.Created}</span><strong style="color:#333;">Last Modified:</strong><span style="color:#333;">${list.LastItemModifiedDate}</span><strong style="color:#333;">Last Deleted:</strong><span style="color:#333;">${list.LastItemDeletedDate}</span><strong style="color:#333;">Major Version Limit:</strong><span style="color:#333;">${list.MajorVersionLimit}</span><strong style="color:#333;">Major with Minor Limit:</strong><span style="color:#333;">${list.MajorWithMinorVersionsLimit}</span><strong style="color:#333;">Draft Version Visibility:</strong><span style="color:#333;">${list.DraftVersionVisibility}</span><strong style="color:#333;">Content Types Enabled:</strong><span style="color:#333;">${list.ContentTypesEnabled}</span><strong style="color:#333;">Attachments Enabled:</strong><span style="color:#333;">${list.EnableAttachments}</span><strong style="color:#333;">Folder Creation:</strong><span style="color:#333;">${list.EnableFolderCreation}</span><strong style="color:#333;">Versioning Enabled:</strong><span style="color:#333;">${list.EnableVersioning}</span><strong style="color:#333;">Minor Versions Enabled:</strong><span style="color:#333;">${list.EnableMinorVersions}</span><strong style="color:#333;">Moderation Enabled:</strong><span style="color:#333;">${list.EnableModeration}</span><strong style="color:#333;">Force Checkout:</strong><span style="color:#333;">${list.ForceCheckout}</span><strong style="color:#333;">Hidden:</strong><span style="color:#333;">${list.Hidden}</span><strong style="color:#333;">IRM Enabled:</strong><span style="color:#333;">${list.IrmEnabled}</span><strong style="color:#333;">Is Catalog:</strong><span style="color:#333;">${list.IsCatalog}</span><strong style="color:#333;">Is Private List:</strong><span style="color:#333;">${list.IsPrivate}</span><strong style="color:#333;">Is System List:</strong><span style="color:#333;">${list.IsSystemList}</span><strong style="color:#333;">Allow Deletion:</strong><span style="color:#333;">${list.AllowDeletion}</span><strong style="color:#333;">Has Unique Permissions:</strong><span style="color:#333;">${list.HasUniqueRoleAssignments}</span><strong style="color:#333;">Web URL:</strong><span style="word-break:break-all;color:#333;">${list.WebUrl}</span><strong style="color:#333;">Web Title:</strong><span style="color:#333;">${list.WebTitle}</span></div><div style="margin-top:20px;text-align:right;"><button id="closeModal" style="padding:8px 16px;background:#0078d4;color:white;border:none;cursor:pointer;">Close</button><button id="openList" style="padding:8px 16px;background:#107c10;color:white;border:none;cursor:pointer;margin-left:10px;">Open List</button><button id="openSettings" style="padding:8px 16px;background:#8b4513;color:white;border:none;cursor:pointer;margin-left:10px;">List Settings</button></div></div>`;modal.innerHTML = modalContent;document.body.appendChild(overlay);document.body.appendChild(modal);// Make modal draggablelet isDragging = false;let currentX;let currentY;let initialX;let initialY;const dragHeader = document.getElementById('modalHeader');// Get initial positionconst rect = modal.getBoundingClientRect();currentX = rect.left + rect.width / 2;currentY = rect.top + rect.height / 2;function dragStart(e) {if (e.target === dragHeader || dragHeader.contains(e.target)) {isDragging = true;initialX = e.clientX - currentX;initialY = e.clientY - currentY;modal.style.transform = 'none';modal.style.left = (currentX - rect.width / 2) + 'px';modal.style.top = (currentY - rect.height / 2) + 'px';}}function dragEnd(e) {isDragging = false;}function drag(e) {if (isDragging) {e.preventDefault();currentX = e.clientX - initialX;currentY = e.clientY - initialY;modal.style.left = (currentX - rect.width / 2) + 'px';modal.style.top = (currentY - rect.height / 2) + 'px';}}dragHeader.addEventListener('mousedown', dragStart);document.addEventListener('mousemove', drag);document.addEventListener('mouseup', dragEnd);// Function to close modalconst closeModal = () => {document.removeEventListener('mousemove', drag);document.removeEventListener('mouseup', dragEnd);document.body.removeChild(modal);document.body.removeChild(overlay);document.removeEventListener('keydown', escHandler);};// Modal event handlersdocument.getElementById('closeModal').onclick = closeModal;document.getElementById('openList').onclick = () => {window.open(list.DefaultViewUrl, '_blank');};document.getElementById('openSettings').onclick = () => {window.open(list.ListSettingsUrl, '_blank');};// Close on overlay click (click away)overlay.onclick = closeModal;// Close on ESC keyconst escHandler = (e) => {if (e.key === 'Escape') {closeModal();}};document.addEventListener('keydown', escHandler);}// Fetch all lists from all webs in site collectionasync function fetchAllLists(webUrl, webTitle = '') {const lists = [];try {// Get lists from current web with comprehensive dataconst listsResponse = await fetch(`${webUrl}/_api/web/lists?$select=Id,Title,Description,ItemCount,RootFolder/ServerRelativeUrl,EntityTypeName,BaseTemplate,BaseType,Created,LastItemModifiedDate,LastItemDeletedDate,DefaultViewUrl,MajorVersionLimit,MajorWithMinorVersionsLimit,DraftVersionVisibility,ContentTypesEnabled,EnableAttachments,EnableFolderCreation,EnableVersioning,EnableMinorVersions,EnableModeration,ForceCheckout,Hidden,IrmEnabled,IsCatalog,IsPrivate,IsSystemList,AllowDeletion,HasUniqueRoleAssignments,ParentWebUrl&$expand=RootFolder`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (listsResponse.ok) {const listsData = await listsResponse.json();const webLists = listsData.d.results;for (const list of webLists) {const listInfo = {Id: list.Id,Title: list.Title,Description: list.Description || '',ItemCount: list.ItemCount,ListUrl: `${webUrl}/Lists/${encodeURIComponent(list.Title)}`,DefaultViewUrl: list.DefaultViewUrl,ListSettingsUrl: `${webUrl}/_layouts/15/listedit.aspx?List={${list.Id}}`,RootFolder: list.RootFolder.ServerRelativeUrl,EntityTypeName: list.EntityTypeName,BaseTemplate: list.BaseTemplate,BaseType: list.BaseType,Created: new Date(list.Created).toLocaleString(),LastItemModifiedDate: list.LastItemModifiedDate ? new Date(list.LastItemModifiedDate).toLocaleString() : 'Never',LastItemDeletedDate: list.LastItemDeletedDate ? new Date(list.LastItemDeletedDate).toLocaleString() : 'Never',MajorVersionLimit: list.MajorVersionLimit || 'N/A',MajorWithMinorVersionsLimit: list.MajorWithMinorVersionsLimit || 'N/A',DraftVersionVisibility: list.DraftVersionVisibility === 0 ? 'Reader' : list.DraftVersionVisibility === 1 ? 'Author' : 'Approver',ContentTypesEnabled: list.ContentTypesEnabled ? 'Yes' : 'No',EnableAttachments: list.EnableAttachments ? 'Yes' : 'No',EnableFolderCreation: list.EnableFolderCreation ? 'Yes' : 'No',EnableVersioning: list.EnableVersioning ? 'Yes' : 'No',EnableMinorVersions: list.EnableMinorVersions ? 'Yes' : 'No',EnableModeration: list.EnableModeration ? 'Yes' : 'No',ForceCheckout: list.ForceCheckout ? 'Yes' : 'No',Hidden: list.Hidden ? 'Yes' : 'No',IrmEnabled: list.IrmEnabled ? 'Yes' : 'No',IsCatalog: list.IsCatalog ? 'Yes' : 'No',IsPrivate: list.IsPrivate ? 'Yes' : 'No',IsSystemList: list.IsSystemList ? 'Yes' : 'No',AllowDeletion: list.AllowDeletion ? 'Yes' : 'No',HasUniqueRoleAssignments: list.HasUniqueRoleAssignments ? 'Yes' : 'No',WebUrl: webUrl,WebTitle: webTitle || webUrl};lists.push(listInfo);}}// Get subsites and their lists recursivelyconst subWebsResponse = await fetch(`${webUrl}/_api/web/webs?$select=Title,Url`, {headers: {'Accept': 'application/json;odata=verbose','Content-Type': 'application/json'},credentials: 'same-origin'});if (subWebsResponse.ok) {const subWebsData = await subWebsResponse.json();const subWebs = subWebsData.d.results;for (const subWeb of subWebs) {const subWebLists = await fetchAllLists(subWeb.Url, subWeb.Title);lists.push(...subWebLists);}}} catch (error) {console.error(`Error fetching lists from ${webUrl}:`, error);}return lists;}// Render lists tablefunction renderLists(filterText = '') {let filteredLists = allListsData;// Filter by hidden statusif (!showHiddenLists) {filteredLists = filteredLists.filter(list => list.Hidden !== 'Yes');}// Filter by search textif (filterText) {filteredLists = filteredLists.filter(list => list.Title.toLowerCase().includes(filterText.toLowerCase()));}let tableHTML = `<table id="listsTable" style="width:100%;border-collapse:collapse;margin-top:10px;user-select:text;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;"><thead><tr style="background:#0078d4;color:white;"><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Title</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">List ID (GUID)</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Web</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Item Count</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Base Template</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Created</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Last Modified</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Hidden</th><th style="border:1px solid #ddd;padding:8px;position:sticky;top:0;background:#0078d4;">Actions</th></tr></thead><tbody>`;filteredLists.forEach((list, index) => {const bgColor = index % 2 === 0 ? '#f2f2f2' : 'white';const rowId = `list_row_${list.Id.replace(/[^a-zA-Z0-9]/g, '_')}`;tableHTML += `<tr data-list-id="${list.Id}" data-bg-color="${bgColor}" style="background:${bgColor};cursor:pointer;"><td class="list-title-cell" style="border:1px solid #ddd;padding:8px;font-weight:bold;color:#0078d4;">${list.Title}</td><td style="border:1px solid #ddd;padding:8px;font-family:monospace;font-size:11px;color:#333;user-select:text;">${list.Id}</td><td style="border:1px solid #ddd;padding:8px;color:#333;">${list.WebTitle}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;color:#333;">${list.ItemCount.toLocaleString()}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;color:#333;">${list.BaseTemplate}</td><td style="border:1px solid #ddd;padding:8px;font-size:11px;color:#333;">${list.Created}</td><td style="border:1px solid #ddd;padding:8px;font-size:11px;color:#333;">${list.LastItemModifiedDate}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;color:#333;">${list.Hidden}</td><td style="border:1px solid #ddd;padding:8px;text-align:center;"><button class="btn-open" data-url="${list.DefaultViewUrl}" style="padding:4px 8px;background:#107c10;color:white;border:none;cursor:pointer;margin-right:5px;font-size:11px;">Open</button><button class="btn-settings" data-url="${list.ListSettingsUrl}" style="padding:4px 8px;background:#8b4513;color:white;border:none;cursor:pointer;font-size:11px;">Settings</button></td></tr>`;});tableHTML += '</tbody></table>';contentContainer.innerHTML = tableHTML;// Add event listeners using delegationconst table = document.getElementById('listsTable');if (table) {// Handle row hovertable.addEventListener('mouseover', function(e) {const row = e.target.closest('tr');if (row && row.dataset.listId) {row.style.background = '#e3f2fd';}});table.addEventListener('mouseout', function(e) {const row = e.target.closest('tr');if (row && row.dataset.listId) {row.style.background = row.dataset.bgColor;}});// Handle clickstable.addEventListener('click', function(e) {// Handle title cell clickif (e.target.classList.contains('list-title-cell')) {const row = e.target.closest('tr');if (row && row.dataset.listId) {const list = allListsData.find(l => l.Id === row.dataset.listId);if (list) {createDetailsModal(list);}}}// Handle Open buttonif (e.target.classList.contains('btn-open')) {e.stopPropagation();const url = e.target.dataset.url;if (url) {window.open(url, '_blank');}}// Handle Settings buttonif (e.target.classList.contains('btn-settings')) {e.stopPropagation();const url = e.target.dataset.url;if (url) {window.open(url, '_blank');}}});}// Update info text with better detailsconst hiddenCount = allListsData.filter(list => list.Hidden === 'Yes').length;const visibleCount = allListsData.length - hiddenCount;if (!showHiddenLists) {infoText.textContent = `Showing ${filteredLists.length} of ${visibleCount} visible lists (${hiddenCount} hidden lists excluded)`;} else {infoText.textContent = `Showing ${filteredLists.length} of ${allListsData.length} lists (${visibleCount} visible, ${hiddenCount} hidden)`;}}// Filter input handlerfilterInput.oninput = (e) => {renderLists(e.target.value);};// Toggle hidden lists handlertoggleHiddenButton.onclick = () => {showHiddenLists = !showHiddenLists;toggleHiddenButton.textContent = showHiddenLists ? '

