From 9b5f921eeb2ad02b8ef9019526f31842f854e13b Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Sat, 7 Oct 2023 18:16:26 +0200 Subject: [PATCH] Add support for custom field names and ids --- .../WebComponent/woltlab-core-label-picker.ts | 6 +- .../files/js/WoltLabSuite/WebComponent.min.js | 2 +- .../lib/system/label/LabelPicker.class.php | 55 ++++++++++++++++++- .../system/label/LabelPickerGroup.class.php | 12 +++- 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/ts/WoltLabSuite/WebComponent/woltlab-core-label-picker.ts b/ts/WoltLabSuite/WebComponent/woltlab-core-label-picker.ts index 916de3453b..f65813a5b7 100644 --- a/ts/WoltLabSuite/WebComponent/woltlab-core-label-picker.ts +++ b/ts/WoltLabSuite/WebComponent/woltlab-core-label-picker.ts @@ -80,7 +80,7 @@ if (this.#formValue === undefined) { this.#formValue = document.createElement("input"); this.#formValue.type = "hidden"; - this.#formValue.name = `labelIDs[${this.dataset.groupId}]`; + this.#formValue.name = `${this.name}[${this.dataset.groupId}]`; this.append(this.#formValue); } @@ -157,6 +157,10 @@ return this.hasAttribute("invertible"); } + get name(): string { + return this.getAttribute("name")!; + } + #getHtmlForNoneLabel(): string { return `${window.WoltLabLanguage.getPhrase("wcf.label.none")}`; } diff --git a/wcfsetup/install/files/js/WoltLabSuite/WebComponent.min.js b/wcfsetup/install/files/js/WoltLabSuite/WebComponent.min.js index ec6d87726d..8e6f7226a2 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/WebComponent.min.js +++ b/wcfsetup/install/files/js/WoltLabSuite/WebComponent.min.js @@ -60,7 +60,7 @@ Expecting `+Z.join(", ")+", got '"+(this.terminals_[E]||E)+"'":ae="Parse error o time::after { content: " (" attr(title) ")"; } - }`,L.append(C)}g&&(this.#e.dateTime=p.toISOString(),this.#e.title=b.DateAndTime.format(p));let y;if(this.static)y=this.#e.title;else if(xt?y=this.#t(L,0):p.getTime()>a?y=this.#t(L,-1):y=L.map(W=>W.value).join(""):y=b.DateAndTime.format(p)}else y=b.Date.format(p);y=y.charAt(0).toUpperCase()+y.slice(1),this.#e.textContent=y}#t(g,p){return g.map(y=>y.type==="weekday"?b.TodayOrYesterday.format(p,"day"):y.value).join("")}}window.customElements.define("woltlab-core-date-time",q);let S=()=>{document.querySelectorAll("woltlab-core-date-time").forEach(h=>h.refresh(!1))},z,P=()=>{z=window.setInterval(()=>{i(),S()},6e4)};document.addEventListener("DOMContentLoaded",()=>P(),{once:!0}),document.addEventListener("visibilitychange",()=>{document.hidden?window.clearInterval(z):(S(),P())})}{class e extends HTMLElement{#a;#e;#t=new Map;constructor(){super(),this.#a=document.createElement("button")}connectedCallback(){if(this.hasAttribute("labels")&&(this.#t=new Map(JSON.parse(this.getAttribute("labels"))),this.removeAttribute("labels")),this.#t.size===0)throw new Error("Expected a non empty list of labels.");let r=this.#i();this.#a.type="button",this.#a.classList.add("dropdownToggle"),this.#a.innerHTML=r,this.#a.addEventListener("click",a=>{a.preventDefault();let i=new CustomEvent("showPicker");this.dispatchEvent(i)}),this.#a.id=this.id,this.removeAttribute("id"),this.append(this.#a);let f=document.createElement("ul");f.classList.add("scrollableDropdownMenu");for(let[a,i]of this.#t)f.append(this.#l(a,i));if(!this.required){let a=document.createElement("li");if(a.classList.add("dropdownDivider"),f.append(a),this.invertible){let i=this.#l(-1,this.#s());f.append(i)}f.append(this.#l(0,r))}let t=document.createElement("ul");t.classList.add("dropdownMenu"),t.append(f),this.append(t),this.classList.add("dropdown"),this.closest("form")!==null?(this.#e===void 0&&(this.#e=document.createElement("input"),this.#e.type="hidden",this.#e.name=`labelIDs[${this.dataset.groupId}]`,this.append(this.#e)),this.#e.value=(this.selected||0).toString()):this.#e?.remove(),this.selected&&this.#r(this.selected)}#l(r,f){let t=document.createElement("button");t.type="button",t.dataset.labelId=r.toString(),t.innerHTML=f,t.addEventListener("click",()=>{this.selected=r});let a=document.createElement("li");return a.append(t),a}set selected(r){this.setAttribute("selected",r.toString()),this.#r(r)}get selected(){let r=parseInt(this.getAttribute("selected"));if(!Number.isNaN(r))return r}set disabled(r){r?this.setAttribute("disabled",""):this.removeAttribute("disabled"),this.#a.disabled=r,this.#e&&(this.#e.disabled=r)}get disabled(){return this.hasAttribute("disabled")}set required(r){r?this.setAttribute("required",""):this.removeAttribute("required")}get required(){return this.hasAttribute("required")}get invertible(){return this.hasAttribute("invertible")}#i(){return`${window.WoltLabLanguage.getPhrase("wcf.label.none")}`}#s(){return`${window.WoltLabLanguage.getPhrase("wcf.label.withoutSelection")}`}#r(r){let f="";this.#t.has(r)?f=this.#t.get(r):r===-1&&this.invertible&&(f=this.#s()),this.#a.innerHTML=f||this.#i(),this.#e!==void 0&&(this.#e.value=r.toString())}}window.customElements.define("woltlab-core-label-picker",e)}{let l=[24,48,96];class r extends HTMLElement{#a;#e;connectedCallback(){this.#a===void 0&&this.#t()}attributeChangedCallback(t,a,i){if(t==="size"){let d=parseInt(i||"");if(!l.includes(d)){let b=parseInt(a||"");l.includes(b)||(b=24),this.setAttribute(t,b.toString())}}}#t(){this.classList.add("loading-indicator"),this.hasAttribute("size")||this.setAttribute("size",24 .toString()),this.#a=document.createElement("fa-icon"),this.#a.size=this.size,this.#a.setIcon("spinner"),this.#e=document.createElement("span"),this.#e.classList.add("loading-indicator__text"),this.#e.textContent=window.WoltLabLanguage.getPhrase("wcf.global.loading"),this.#e.hidden=this.hideText;let t=document.createElement("div");t.classList.add("loading-indicator__wrapper"),t.append(this.#a,this.#e),this.append(t)}get size(){return parseInt(this.getAttribute("size"))}set size(t){if(!l.includes(t))throw new TypeError(`The size ${t} is unrecognized, permitted values are ${l.join(", ")}.`);this.setAttribute("size",t.toString()),this.#a&&(this.#a.size=t)}get hideText(){return this.hasAttribute("hide-text")}set hideText(t){t?this.setAttribute("hide-text",""):this.removeAttribute("hide-text"),this.#e&&(this.#e.hidden=t)}static get observedAttributes(){return["size"]}}window.customElements.define("woltlab-core-loading-indicator",r)}{let e,l=()=>(e===void 0&&(e=window.matchMedia("(max-width: 544px)")),e);class r extends HTMLElement{#a="pagination";connectedCallback(){this.#e(),l().addEventListener("change",()=>this.#e())}#e(){if(this.innerHTML="",this.count<2)return;this.classList.add(`${this.#a}__wrapper`);let t=this.#t();this.append(t);let a=this.#l();a&&t.append(a);let i=document.createElement("ul");i.classList.add(`${this.#a}__list`),t.append(i),i.append(this.#r(1)),this.page>this.thresholdForEllipsis+1&&i.append(this.#n()),this.#f().forEach(b=>{i.append(b)}),this.count-this.page>this.thresholdForEllipsis&&i.append(this.#n()),i.append(this.#r(this.count));let d=this.#i();d&&t.append(d)}#t(){let t=document.createElement("nav");return t.setAttribute("role","navigation"),t.setAttribute("aria-label",window.WoltLabLanguage.getPhrase("wcf.page.pagination")),t.classList.add(this.#a),t}#l(){if(this.page===1)return;let t=document.createElement("div");t.classList.add(`${this.#a}__prev`);let a=this.#s(this.page-1);a instanceof HTMLAnchorElement&&(a.rel="prev"),a.title=window.WoltLabLanguage.getPhrase("wcf.global.page.previous"),a.classList.add("jsTooltip"),t.append(a);let i=document.createElement("fa-icon");return i.setIcon("arrow-left"),a.append(i),t}#i(){if(this.page===this.count)return;let t=document.createElement("div");t.classList.add(`${this.#a}__next`);let a=this.#s(this.page+1);a instanceof HTMLAnchorElement&&(a.rel="next"),a.title=window.WoltLabLanguage.getPhrase("wcf.global.page.next"),a.classList.add("jsTooltip"),t.append(a);let i=document.createElement("fa-icon");return i.setIcon("arrow-right"),a.append(i),t}#s(t){let a,i=this.getLinkUrl(t);return i?(a=document.createElement("a"),a.href=i):(a=document.createElement("button"),a.type="button",this.page===t?a.disabled=!0:a.addEventListener("click",()=>{this.#o(t)})),a.classList.add(`${this.#a}__link`),a}#r(t){let a=document.createElement("li");a.classList.add(`${this.#a}__item`);let i=this.#s(t);return i.setAttribute("aria-label",window.WoltLabLanguage.getPhrase("wcf.page.pageNo",{pageNo:t})),t===this.page&&(i.setAttribute("aria-current","page"),i.classList.add(`${this.#a}__link--current`)),i.textContent=t.toLocaleString(document.documentElement.lang),a.append(i),a}#f(){let t=[],a,i;l().matches?(a=this.page,i=this.page):(a=this.page-1,a===3&&a--,i=this.page+1,i===this.count-2&&i++);for(let d=a;d<=i;d++)d<=1||d>=this.count||t.push(this.#r(d));return t}#n(){let t=document.createElement("li");t.classList.add(`${this.#a}__item`,`${this.#a}__item--ellipsis`);let a=document.createElement("button");return a.type="button",a.title=window.WoltLabLanguage.getPhrase("wcf.page.jumpTo"),a.classList.add("pagination__link","jsTooltip"),a.innerHTML="⋯",a.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("jumpToPage"))}),t.append(a),t}get thresholdForEllipsis(){return l().matches?1:3}getLinkUrl(t){if(!this.url)return"";let a=new URL(this.url);return a.search+=a.search!==""?"&":"?",a.search+=new URLSearchParams([["pageNo",t.toString()]]).toString(),a.toString()}jumpToPage(t){let a=this.getLinkUrl(t);a?window.location.href=a:this.#o(t)}#o(t){let a=new CustomEvent("switchPage",{cancelable:!0,detail:t});this.dispatchEvent(a),a.defaultPrevented||(this.page=t)}get count(){return this.hasAttribute("count")?parseInt(this.getAttribute("count")):0}set count(t){this.setAttribute("count",t.toString()),this.#e()}get page(){return this.hasAttribute("page")?parseInt(this.getAttribute("page")):1}set page(t){this.setAttribute("page",t.toString()),this.#e()}get url(){return this.getAttribute("url")}set url(t){this.setAttribute("url",t),this.#e()}}window.customElements.define("woltlab-core-pagination",r)}{class e extends HTMLElement{connectedCallback(){this.setData(this.#e(),this.#t())}setData(r,f){this.#a(r,f)}get objectId(){return parseInt(this.getAttribute("object-id"))}get objectType(){return this.getAttribute("object-type")}#a(r,f){if(this.innerHTML="",!r.size)return;let t=document.createElement("button");t.classList.add("reactionSummary","jsTooltip"),t.title=window.WoltLabLanguage.getPhrase("wcf.reactions.summary.listReactions"),t.addEventListener("click",()=>{this.dispatchEvent(new Event("showDetails"))}),this.append(t),r.forEach((a,i)=>{let d=document.createElement("span");d.classList.add("reactionCountButton"),i===f&&d.classList.add("selected");let b=document.createElement("span");b.innerHTML=window.REACTION_TYPES[i].renderedIcon,d.append(b);let k=document.createElement("span");k.classList.add("reactionCount"),k.textContent=a.toString(),d.append(k),t.append(d)})}#e(){let r=JSON.parse(this.getAttribute("data"));return this.removeAttribute("data"),new Map(r)}#t(){return parseInt(this.getAttribute("selected-reaction"))}}window.customElements.define("woltlab-core-reaction-summary",e)}window.WoltLabLanguage=ne;window.WoltLabTemplate=V;window.HTMLParsedElement=pe;})(); + }`,L.append(C)}g&&(this.#e.dateTime=p.toISOString(),this.#e.title=b.DateAndTime.format(p));let y;if(this.static)y=this.#e.title;else if(xt?y=this.#t(L,0):p.getTime()>a?y=this.#t(L,-1):y=L.map(W=>W.value).join(""):y=b.DateAndTime.format(p)}else y=b.Date.format(p);y=y.charAt(0).toUpperCase()+y.slice(1),this.#e.textContent=y}#t(g,p){return g.map(y=>y.type==="weekday"?b.TodayOrYesterday.format(p,"day"):y.value).join("")}}window.customElements.define("woltlab-core-date-time",q);let S=()=>{document.querySelectorAll("woltlab-core-date-time").forEach(h=>h.refresh(!1))},z,P=()=>{z=window.setInterval(()=>{i(),S()},6e4)};document.addEventListener("DOMContentLoaded",()=>P(),{once:!0}),document.addEventListener("visibilitychange",()=>{document.hidden?window.clearInterval(z):(S(),P())})}{class e extends HTMLElement{#a;#e;#t=new Map;constructor(){super(),this.#a=document.createElement("button")}connectedCallback(){if(this.hasAttribute("labels")&&(this.#t=new Map(JSON.parse(this.getAttribute("labels"))),this.removeAttribute("labels")),this.#t.size===0)throw new Error("Expected a non empty list of labels.");let r=this.#i();this.#a.type="button",this.#a.classList.add("dropdownToggle"),this.#a.innerHTML=r,this.#a.addEventListener("click",a=>{a.preventDefault();let i=new CustomEvent("showPicker");this.dispatchEvent(i)}),this.#a.id=this.id,this.removeAttribute("id"),this.append(this.#a);let f=document.createElement("ul");f.classList.add("scrollableDropdownMenu");for(let[a,i]of this.#t)f.append(this.#l(a,i));if(!this.required){let a=document.createElement("li");if(a.classList.add("dropdownDivider"),f.append(a),this.invertible){let i=this.#l(-1,this.#s());f.append(i)}f.append(this.#l(0,r))}let t=document.createElement("ul");t.classList.add("dropdownMenu"),t.append(f),this.append(t),this.classList.add("dropdown"),this.closest("form")!==null?(this.#e===void 0&&(this.#e=document.createElement("input"),this.#e.type="hidden",this.#e.name=`${this.name}[${this.dataset.groupId}]`,this.append(this.#e)),this.#e.value=(this.selected||0).toString()):this.#e?.remove(),this.selected&&this.#r(this.selected)}#l(r,f){let t=document.createElement("button");t.type="button",t.dataset.labelId=r.toString(),t.innerHTML=f,t.addEventListener("click",()=>{this.selected=r});let a=document.createElement("li");return a.append(t),a}set selected(r){this.setAttribute("selected",r.toString()),this.#r(r)}get selected(){let r=parseInt(this.getAttribute("selected"));if(!Number.isNaN(r))return r}set disabled(r){r?this.setAttribute("disabled",""):this.removeAttribute("disabled"),this.#a.disabled=r,this.#e&&(this.#e.disabled=r)}get disabled(){return this.hasAttribute("disabled")}set required(r){r?this.setAttribute("required",""):this.removeAttribute("required")}get required(){return this.hasAttribute("required")}get invertible(){return this.hasAttribute("invertible")}get name(){return this.getAttribute("name")}#i(){return`${window.WoltLabLanguage.getPhrase("wcf.label.none")}`}#s(){return`${window.WoltLabLanguage.getPhrase("wcf.label.withoutSelection")}`}#r(r){let f="";this.#t.has(r)?f=this.#t.get(r):r===-1&&this.invertible&&(f=this.#s()),this.#a.innerHTML=f||this.#i(),this.#e!==void 0&&(this.#e.value=r.toString())}}window.customElements.define("woltlab-core-label-picker",e)}{let l=[24,48,96];class r extends HTMLElement{#a;#e;connectedCallback(){this.#a===void 0&&this.#t()}attributeChangedCallback(t,a,i){if(t==="size"){let d=parseInt(i||"");if(!l.includes(d)){let b=parseInt(a||"");l.includes(b)||(b=24),this.setAttribute(t,b.toString())}}}#t(){this.classList.add("loading-indicator"),this.hasAttribute("size")||this.setAttribute("size",24 .toString()),this.#a=document.createElement("fa-icon"),this.#a.size=this.size,this.#a.setIcon("spinner"),this.#e=document.createElement("span"),this.#e.classList.add("loading-indicator__text"),this.#e.textContent=window.WoltLabLanguage.getPhrase("wcf.global.loading"),this.#e.hidden=this.hideText;let t=document.createElement("div");t.classList.add("loading-indicator__wrapper"),t.append(this.#a,this.#e),this.append(t)}get size(){return parseInt(this.getAttribute("size"))}set size(t){if(!l.includes(t))throw new TypeError(`The size ${t} is unrecognized, permitted values are ${l.join(", ")}.`);this.setAttribute("size",t.toString()),this.#a&&(this.#a.size=t)}get hideText(){return this.hasAttribute("hide-text")}set hideText(t){t?this.setAttribute("hide-text",""):this.removeAttribute("hide-text"),this.#e&&(this.#e.hidden=t)}static get observedAttributes(){return["size"]}}window.customElements.define("woltlab-core-loading-indicator",r)}{let e,l=()=>(e===void 0&&(e=window.matchMedia("(max-width: 544px)")),e);class r extends HTMLElement{#a="pagination";connectedCallback(){this.#e(),l().addEventListener("change",()=>this.#e())}#e(){if(this.innerHTML="",this.count<2)return;this.classList.add(`${this.#a}__wrapper`);let t=this.#t();this.append(t);let a=this.#l();a&&t.append(a);let i=document.createElement("ul");i.classList.add(`${this.#a}__list`),t.append(i),i.append(this.#r(1)),this.page>this.thresholdForEllipsis+1&&i.append(this.#n()),this.#f().forEach(b=>{i.append(b)}),this.count-this.page>this.thresholdForEllipsis&&i.append(this.#n()),i.append(this.#r(this.count));let d=this.#i();d&&t.append(d)}#t(){let t=document.createElement("nav");return t.setAttribute("role","navigation"),t.setAttribute("aria-label",window.WoltLabLanguage.getPhrase("wcf.page.pagination")),t.classList.add(this.#a),t}#l(){if(this.page===1)return;let t=document.createElement("div");t.classList.add(`${this.#a}__prev`);let a=this.#s(this.page-1);a instanceof HTMLAnchorElement&&(a.rel="prev"),a.title=window.WoltLabLanguage.getPhrase("wcf.global.page.previous"),a.classList.add("jsTooltip"),t.append(a);let i=document.createElement("fa-icon");return i.setIcon("arrow-left"),a.append(i),t}#i(){if(this.page===this.count)return;let t=document.createElement("div");t.classList.add(`${this.#a}__next`);let a=this.#s(this.page+1);a instanceof HTMLAnchorElement&&(a.rel="next"),a.title=window.WoltLabLanguage.getPhrase("wcf.global.page.next"),a.classList.add("jsTooltip"),t.append(a);let i=document.createElement("fa-icon");return i.setIcon("arrow-right"),a.append(i),t}#s(t){let a,i=this.getLinkUrl(t);return i?(a=document.createElement("a"),a.href=i):(a=document.createElement("button"),a.type="button",this.page===t?a.disabled=!0:a.addEventListener("click",()=>{this.#o(t)})),a.classList.add(`${this.#a}__link`),a}#r(t){let a=document.createElement("li");a.classList.add(`${this.#a}__item`);let i=this.#s(t);return i.setAttribute("aria-label",window.WoltLabLanguage.getPhrase("wcf.page.pageNo",{pageNo:t})),t===this.page&&(i.setAttribute("aria-current","page"),i.classList.add(`${this.#a}__link--current`)),i.textContent=t.toLocaleString(document.documentElement.lang),a.append(i),a}#f(){let t=[],a,i;l().matches?(a=this.page,i=this.page):(a=this.page-1,a===3&&a--,i=this.page+1,i===this.count-2&&i++);for(let d=a;d<=i;d++)d<=1||d>=this.count||t.push(this.#r(d));return t}#n(){let t=document.createElement("li");t.classList.add(`${this.#a}__item`,`${this.#a}__item--ellipsis`);let a=document.createElement("button");return a.type="button",a.title=window.WoltLabLanguage.getPhrase("wcf.page.jumpTo"),a.classList.add("pagination__link","jsTooltip"),a.innerHTML="⋯",a.addEventListener("click",()=>{this.dispatchEvent(new CustomEvent("jumpToPage"))}),t.append(a),t}get thresholdForEllipsis(){return l().matches?1:3}getLinkUrl(t){if(!this.url)return"";let a=new URL(this.url);return a.search+=a.search!==""?"&":"?",a.search+=new URLSearchParams([["pageNo",t.toString()]]).toString(),a.toString()}jumpToPage(t){let a=this.getLinkUrl(t);a?window.location.href=a:this.#o(t)}#o(t){let a=new CustomEvent("switchPage",{cancelable:!0,detail:t});this.dispatchEvent(a),a.defaultPrevented||(this.page=t)}get count(){return this.hasAttribute("count")?parseInt(this.getAttribute("count")):0}set count(t){this.setAttribute("count",t.toString()),this.#e()}get page(){return this.hasAttribute("page")?parseInt(this.getAttribute("page")):1}set page(t){this.setAttribute("page",t.toString()),this.#e()}get url(){return this.getAttribute("url")}set url(t){this.setAttribute("url",t),this.#e()}}window.customElements.define("woltlab-core-pagination",r)}{class e extends HTMLElement{connectedCallback(){this.setData(this.#e(),this.#t())}setData(r,f){this.#a(r,f)}get objectId(){return parseInt(this.getAttribute("object-id"))}get objectType(){return this.getAttribute("object-type")}#a(r,f){if(this.innerHTML="",!r.size)return;let t=document.createElement("button");t.classList.add("reactionSummary","jsTooltip"),t.title=window.WoltLabLanguage.getPhrase("wcf.reactions.summary.listReactions"),t.addEventListener("click",()=>{this.dispatchEvent(new Event("showDetails"))}),this.append(t),r.forEach((a,i)=>{let d=document.createElement("span");d.classList.add("reactionCountButton"),i===f&&d.classList.add("selected");let b=document.createElement("span");b.innerHTML=window.REACTION_TYPES[i].renderedIcon,d.append(b);let k=document.createElement("span");k.classList.add("reactionCount"),k.textContent=a.toString(),d.append(k),t.append(d)})}#e(){let r=JSON.parse(this.getAttribute("data"));return this.removeAttribute("data"),new Map(r)}#t(){return parseInt(this.getAttribute("selected-reaction"))}}window.customElements.define("woltlab-core-reaction-summary",e)}window.WoltLabLanguage=ne;window.WoltLabTemplate=V;window.HTMLParsedElement=pe;})(); /** * Handles the low level management of language items. * diff --git a/wcfsetup/install/files/lib/system/label/LabelPicker.class.php b/wcfsetup/install/files/lib/system/label/LabelPicker.class.php index ea874bbae2..52b19aada0 100644 --- a/wcfsetup/install/files/lib/system/label/LabelPicker.class.php +++ b/wcfsetup/install/files/lib/system/label/LabelPicker.class.php @@ -23,8 +23,15 @@ final class LabelPicker */ public readonly bool $invertible; + /** + * Name of the hidden input field. + */ + public string $name = 'labelIDs'; + public readonly ViewableLabelGroup $labelGroup; + private string $elementID; + private int $selected = 0; public function __construct(ViewableLabelGroup $labelGroup, bool $invertible) @@ -33,6 +40,11 @@ final class LabelPicker $this->invertible = $invertible; } + /** + * Sets the selected label of this label picker by providing its id. The + * value `0` indicates that no selection is to be made and `-1` inverts the + * selection of the labels. + */ public function setSelectedValue(int $selected): void { if ($selected === 0) { @@ -44,16 +56,28 @@ final class LabelPicker } } + /** + * The returned value can be `0` to indicate that no selection has been + * made, `-1` to indicate that the selection should be inverted or the id + * of the selected label. + */ public function getSelectedValue(): int { return $this->selected; } + /** + * Returns true if a label has been selected or if the selection has been + * inverted. + */ public function hasSelection(): bool { return $this->selected !== 0; } + /** + * Generates the HTML element for the label picker. + */ public function toHtml(): string { $labels = []; @@ -70,21 +94,48 @@ final class LabelPicker id="%s" title="%s" labels="%s" + name="%s" data-group-id="%d" %s > EOT, $this->selected, $this->getElementID(), - $this->labelGroup->getTitle(), + StringUtil::encodeHTML($this->labelGroup->getTitle()), StringUtil::encodeHTML(JSON::encode($labels)), + StringUtil::encodeHTML($this->name), $this->labelGroup->groupID, $this->invertible ? 'invertible' : '', ); } + /** + * Returns the unique element id of this label picker. + */ public function getElementID(): string { - return "labelGroup{$this->labelGroup->groupID}"; + if (!isset($this->elementID)) { + $this->elementID = \sprintf( + '%s_labelGroup%d', + \substr(\md5($this->name), 0, 8), + $this->labelGroup->groupID, + ); + } + + return $this->elementID; + } + + /** + * Sets the unique element id of this label picker. Must be set before + * attempting to read the element id which is implicitly done by calling + * `toHtml()`. + */ + public function setElementID(string $elementID): void + { + if (isset($this->elementID)) { + throw new \RuntimeException("Cannot set the element id, already set."); + } + + $this->elementID = $elementID; } } diff --git a/wcfsetup/install/files/lib/system/label/LabelPickerGroup.class.php b/wcfsetup/install/files/lib/system/label/LabelPickerGroup.class.php index f26d5d1006..273a6daf11 100644 --- a/wcfsetup/install/files/lib/system/label/LabelPickerGroup.class.php +++ b/wcfsetup/install/files/lib/system/label/LabelPickerGroup.class.php @@ -45,7 +45,7 @@ final class LabelPickerGroup implements \Countable, \Iterator } $this->labelPickers = $pickers; - $this->positionToGroupID = \array_keys($labelPickers); + $this->positionToGroupID = \array_keys($pickers); } /** @@ -193,6 +193,16 @@ final class LabelPickerGroup implements \Countable, \Iterator } } + /** + * Set the name of the hidden input field for the label ids. + */ + public function setName(string $name): void + { + foreach ($this->labelPickers as $labelPicker) { + $labelPicker->name = $name; + } + } + public function count(): int { return \count($this->labelPickers); -- 2.20.1