CVE-2023-33197: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Craft is a CMS for creating custom digital experiences on the web. Cross-site scripting (XSS) can be triggered via the Update Asset Index utility. This issue has been patched in version 4.4.6.

CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-05-26T20:15Z
Repository: https://github.com/craftcms/cms
Language: JavaScript
Files changed: CHANGELOG.md, src/web/assets/assetindexes/dist/AssetIndexer.js, src/web/assets/assetindexes/dist/AssetIndexer.js.map, src/web/assets/assetindexes/src/AssetIndexer.ts
Commit date: 2023-04-03 15:12:47 -0700
Commit message: Fixed an XSS vulnerability

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0d2a4ee655..f1ef916512f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,10 +16,11 @@
 - Fixed a bug where the wrong tab would appear to be initially selected after an autosave, if the selected tab had changed during the autosave. ([#12960](https://github.com/craftcms/cms/issues/12960))
 - Fixed a bug where it wasn’t possible to add a Dropdown field without a blank option to a global set. ([#12965](https://github.com/craftcms/cms/issues/12965))
 - Fixed a bug where automatically-added Matrix blocks (per the field’s Min Blocks setting) were getting discarded if no changes were made to them. ([#12973](https://github.com/craftcms/cms/issues/12973))
 - Fixed an error that could occur when installing Craft with an existing project config, if any image transforms were defined that didn’t specify the `upscale` property.
 - Fixed a bug where nested folders in asset search results weren’t showing their relative path.
+- Fixed an XSS vulnerability.
 
 ## 4.4.5 - 2023-03-21
 
 - Fixed a bug where relation data was getting deleted when running garbage collection on PostgreSQL. ([#9905](https://github.com/craftcms/cms/issues/9905))
 - Fixed a bug where Lightswitch fields’ “OFF Label” and “ON Label” settings weren’t getting translated. ([#12942](https://github.com/craftcms/cms/issues/12942))
diff --git a/src/web/assets/assetindexes/dist/AssetIndexer.js b/src/web/assets/assetindexes/dist/AssetIndexer.js
index 47e83dde851..bdb122f7077 100644
--- a/src/web/assets/assetindexes/dist/AssetIndexer.js
+++ b/src/web/assets/assetindexes/dist/AssetIndexer.js
@@ -1,2 +1,2 @@
-!function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),alert(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),c=$('<footer class="footer"/>').appendTo(d),h=$('<div class="buttons right"/>').appendTo(c),u=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),u.hide()})).appendTo(h),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(h)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(h),Craft.initUiElements(g),u.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),u.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">');s.append("<td><ul><li>"+Object.values(this.indexingSessionData.indexedVolumes).join("</li><li>")+"</li></ul></td>"),s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const e=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),n=new Craft.ProgressBar(e.find(".progressContainer"),!1);n.setItemCount(this.indexingSessionData.totalEntries),n.setProcessedItemCount(this.indexingSessionData.processedEntries),n.updateProgressBar(),n.showProgressBar(),e.data("progressBar",n),e.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(e),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const t=this.getActionButtons();return $("<td></td>").append(t).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
+!function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),alert(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),c=$('<footer class="footer"/>').appendTo(d),p=$('<div class="buttons right"/>').appendTo(c),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(p),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(p)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(p),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),n=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(n);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const t=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(t.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),t.data("progressBar",i),t.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(t),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
 //# sourceMappingURL=AssetIndexer.js.map
\ No newline at end of file
diff --git a/src/web/assets/assetindexes/dist/AssetIndexer.js.map b/src/web/assets/assetindexes/dist/AssetIndexer.js.map
index 68d5bd89d16..ae0e1fdead1 100644
--- a/src/web/assets/assetindexes/dist/AssetIndexer.js.map
+++ b/src/web/assets/assetindexes/dist/AssetIndexer.js.map
@@ -1 +1 @@
-{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        alert(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        $tr.append('<td><ul><li>' +\n            Object.values(this.indexingSessionData.indexedVolumes).join('</li><li>') +\n            '</li></ul></td>');\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","alert","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","Craft","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","indexedVolumes","join","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        alert(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","alert","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","Craft","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/web/assets/assetindexes/src/AssetIndexer.ts b/src/web/assets/assetindexes/src/AssetIndexer.ts
index 220a8328af1..104a68f0896 100644
--- a/src/web/assets/assetindexes/src/AssetIndexer.ts
+++ b/src/web/assets/assetindexes/src/AssetIndexer.ts
@@ -676,17 +676,19 @@ class AssetIndexingSession {
     const $tr = $(
       '<tr class="indexingSession" data-session-id="' +
         this.getSessionId() +
         '">'
     );
-    $tr.append(
-      '<td><ul><li>' +
-        Object.values(this.indexingSessionData.indexedVolumes).join(
-          '</li><li>'
-        ) +
-        '</li></ul></td>'
-    );
+    const $td = $('<td/>').appendTo($tr);
+    const $ul = $('<ul/>').appendTo($td);
+    for (const volume of Object.values(
+      this.indexingSessionData.indexedVolumes
+    )) {
+      $('<li/>', {
+        text: volume,
+      }).appendTo($ul);
+    }
     $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');
 
     const $progressCell = $(
       '<td class="progress"><div class="progressContainer"></div></td>'
     ).css('position', 'relative');

Vulnerable code: !function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),alert(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),c=$('<footer class="footer"/>').appendTo(d),h=$('<div class="buttons right"/>').appendTo(c),u=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),u.hide()})).appendTo(h),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(h)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(h),Craft.initUiElements(g),u.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),u.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">');s.append("<td><ul><li>"+Object.values(this.indexingSessionData.indexedVolumes).join("</li><li>")+"</li></ul></td>"),s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const e=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),n=new Craft.ProgressBar(e.find(".progressContainer"),!1);n.setItemCount(this.indexingSessionData.totalEntries),n.setProcessedItemCount(this.indexingSessionData.processedEntries),n.updateProgressBar(),n.showProgressBar(),e.data("progressBar",n),e.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(e),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const t=this.getActionButtons();return $("<td></td>").append(t).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        alert(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        $tr.append('<td><ul><li>' +\n            Object.values(this.indexingSessionData.indexedVolumes).join('</li><li>') +\n            '</li></ul></td>');\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","alert","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","Craft","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","indexedVolumes","join","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
    $tr.append(
      '<td><ul><li>' +
        Object.values(this.indexingSessionData.indexedVolumes).join(
          '</li><li>'
        ) +
        '</li></ul></td>'
    );
Fixed code: - Fixed an XSS vulnerability.
!function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),alert(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),c=$('<footer class="footer"/>').appendTo(d),p=$('<div class="buttons right"/>').appendTo(c),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(p),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(p)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(p),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),n=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(n);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const t=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(t.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),t.data("progressBar",i),t.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(t),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        alert(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","alert","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","Craft","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
    const $td = $('<td/>').appendTo($tr);
    const $ul = $('<ul/>').appendTo($td);
    for (const volume of Object.values(
      this.indexingSessionData.indexedVolumes
    )) {
      $('<li/>', {
        text: volume,
      }).appendTo($ul);
    }