diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67421cd..8bdbe85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,32 @@
# Changelog
+## [v0.18.0] - 2026-09-23
+
+- Change: use correct File component props
+- Change: use creation date as fallback for last update date in File component
+
+## [v0.17.0] - 2026-09-22
+
+- Added: use placeholder component in owc-my-services/zaak block
+- Added: filter zaken on configured Zaaktypen
+- Added: split informatieobjecttype filtering per supplier
+- Fix: bsnKvkMissing reads disableKVKFiltering one line before its const declaration
+- Fix: re-sort merged zaken by orderBy before truncating to perPage
+
+## [v0.16.0] - 2026-09-09
+
+- Change: remove deprecated plain KVK-number filter, always filter by vestigingsNummer/RSIN when available
+- Change: remove 'Uitgebreide KVK-filtering inschakelen' setting (now always active)
+
+## [v0.15.1] - 2026-09-07
+
+- Feat: tabs toggle, editor panel rework, empty-tab message
+
+## [v0.15.0] - 2026-09-01
+
+- Change: enabled settings by filtered capability
+- Change: return type of Zaak macro endDate
+
## [v0.14.0] - 2026-08-19
- Chore: update owc/zgw-api package
diff --git a/build/css/informatieobjecttypen-select.css b/build/css/informatieobjecttypen-select.css
new file mode 100644
index 0000000..0730bf0
--- /dev/null
+++ b/build/css/informatieobjecttypen-select.css
@@ -0,0 +1 @@
+#owc-mijn-services-allowed-informatieobjecttypen+.select2-container .select2-selection--multiple .select2-selection__rendered{display:flex;flex-wrap:wrap;align-items:center;padding:5px;gap:4px}#owc-mijn-services-allowed-informatieobjecttypen+.select2-container .select2-selection--multiple .select2-selection__choice{margin:0;float:none}#owc-mijn-services-allowed-informatieobjecttypen+.select2-container .select2-search--inline{margin:0;float:none}#owc-mijn-services-allowed-informatieobjecttypen+.select2-container .select2-search--inline .select2-search__field{height:auto;min-height:0;padding:0;margin:0;box-shadow:none;line-height:normal}#owc-mijn-services-allowed-informatieobjecttypen+.select2-container--default .select2-selection--multiple .select2-selection__clear{margin-top:0}
diff --git a/build/css/informatieobjecttypen-select.js b/build/css/informatieobjecttypen-select.js
new file mode 100644
index 0000000..e69de29
diff --git a/build/mijn-zaken/block.json b/build/mijn-zaken/block.json
index acd5b93..c923b8f 100644
--- a/build/mijn-zaken/block.json
+++ b/build/mijn-zaken/block.json
@@ -14,6 +14,10 @@
"type": "array",
"default": []
},
+ "zaaktypen": {
+ "type": "object",
+ "default": {}
+ },
"byBSN": {
"type": "boolean",
"default": true
@@ -22,6 +26,10 @@
"type": "boolean",
"default": false
},
+ "showTabs": {
+ "type": "boolean",
+ "default": true
+ },
"perPage": {
"type": "number",
"default": 10
diff --git a/build/mijn-zaken/index-rtl.css b/build/mijn-zaken/index-rtl.css
index 8b13789..ac6b413 100644
--- a/build/mijn-zaken/index-rtl.css
+++ b/build/mijn-zaken/index-rtl.css
@@ -1 +1 @@
-
+.owc-my-services-zaaktypen-supplier-panel.components-panel__body{border:1px solid #ddd;margin:0 0 8px}
diff --git a/build/mijn-zaken/index.asset.php b/build/mijn-zaken/index.asset.php
index 25331a9..44750a9 100644
--- a/build/mijn-zaken/index.asset.php
+++ b/build/mijn-zaken/index.asset.php
@@ -1 +1 @@
- array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '6de45742170e11633a46');
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'c643d1ab2f1e94445f44');
diff --git a/build/mijn-zaken/index.css b/build/mijn-zaken/index.css
index 8b13789..ac6b413 100644
--- a/build/mijn-zaken/index.css
+++ b/build/mijn-zaken/index.css
@@ -1 +1 @@
-
+.owc-my-services-zaaktypen-supplier-panel.components-panel__body{border:1px solid #ddd;margin:0 0 8px}
diff --git a/build/mijn-zaken/index.js b/build/mijn-zaken/index.js
index fc79701..16e85af 100644
--- a/build/mijn-zaken/index.js
+++ b/build/mijn-zaken/index.js
@@ -1 +1 @@
-(()=>{"use strict";var e,i={219:(e,i,t)=>{const n=window.wp.blocks,a=window.wp.element,l=window.wp.blockEditor,r=window.wp.components,o=window.wp.serverSideRender;var s=t.n(o);const c=window.wp.i18n,d=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"owc-my-services/mijn-zaken","version":"1.0.0","title":"OWC Mijn Zaken","description":"Toont Mijn Zaken blok.","attributes":{"zaakClient":{"type":"string","default":""},"zaakClients":{"type":"array","default":[]},"byBSN":{"type":"boolean","default":true},"byKVK":{"type":"boolean","default":false},"perPage":{"type":"number","default":10},"orderBy":{"type":"string","default":"startdatum"},"orderByDirection":{"type":"string","default":"-"}},"supports":{"html":false},"textdomain":"owc-my-services","editorScript":"file:./index.js","editorStyle":"file:./editor.css","style":"file:./style-index.css","viewScript":"file:./view.js"}'),u=window.ReactJSXRuntime;(0,n.registerBlockType)(d,{edit:function({attributes:e,setAttributes:i}){const{zaakClient:t,zaakClients:n,byBSN:o,byKVK:p,perPage:v,orderBy:b,orderByDirection:m}=e,w=(window?.owcMyServices?.zaakClientOptions??[]).filter((e=>""!==e.value)),h=(window?.owcMyServices?.productionChecksEnabled??!0)&&!o&&!(p&&!g),g=window?.owcMyServices?.disableKVKFiltering??!1;return(0,a.useEffect)((()=>{t&&0===n.length&&i({zaakClients:[t],zaakClient:""})}),[]),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(l.InspectorControls,{children:(0,u.jsxs)(r.PanelBody,{title:(0,c.__)("Instellingen","owc-mijn-services"),initialOpen:!0,children:[w.length>0?(0,u.jsx)(r.BaseControl,{id:"owc-my-services-zaak-clients-checkbox-selection",label:(0,c.__)("Zaaksystemen","owc-mijn-services"),help:(0,c.__)("Selecteer de zaaksystemen waaruit de zaken opgehaald moeten worden.","owc-mijn-services"),children:w.map((e=>(0,u.jsx)(r.CheckboxControl,{label:e.label,checked:n.includes(e.value),onChange:t=>{const a=t?[...n,e.value]:n.filter((i=>i!==e.value));i({zaakClients:a})}},e.value)))}):(0,u.jsx)("p",{children:(0,c.__)("Geen leveranciers geconfigureerd.","owc-mijn-services")}),h&&(0,u.jsx)(r.Notice,{status:"warning",isDismissible:!1,children:g?(0,c.__)("Schakel de filteroptie Filter op BSN in.","owc-mijn-services"):(0,c.__)("Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK.","owc-mijn-services")}),(0,u.jsx)(r.CheckboxControl,{label:"Filter op BSN",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het BSN nummer.",checked:o,onChange:e=>i({byBSN:e})}),!g&&(0,u.jsx)(r.CheckboxControl,{label:"Filter op KVK",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het KVK nummer.",checked:p,onChange:e=>i({byKVK:e})}),g&&(0,u.jsx)(r.Notice,{status:"warning",isDismissible:!1,children:(0,c.__)("Filteren op KVK is uitgeschakeld in de instellingen van de plugin.","owc-mijn-services")}),(0,u.jsx)(r.RangeControl,{label:(0,c.__)("Aantal zaken","owc-mijn-services"),value:v,min:1,max:25,onChange:e=>i({perPage:e})}),(0,u.jsx)(r.SelectControl,{label:"Sorteer op",value:b,options:[{label:"Selecteer een optie",value:""},{label:"Startdatum",value:"startdatum"},{label:"Einddatum",value:"einddatum"},{label:"Publicatiedatum",value:"publicatiedatum"},{label:"Archiefactiedatum",value:"archiefactiedatum"},{label:"Registratiedatum",value:"registratiedatum"},{label:"Identificatie",value:"identificatie"}],onChange:e=>i({orderBy:e})}),b&&""!==b&&(0,u.jsx)(r.SelectControl,{label:"Sorteer volgorde",value:m,options:[{label:"Oplopend",value:"+"},{label:"Aflopend",value:"-"}],onChange:e=>i({orderByDirection:e})})]})}),(0,u.jsx)("div",{...(0,l.useBlockProps)(),children:(0,u.jsx)(r.Disabled,{children:(0,u.jsx)(s(),{block:d.name,attributes:e})})})]})},save:function(){return null}})}},t={};function n(e){var a=t[e];if(void 0!==a)return a.exports;var l=t[e]={exports:{}};return i[e](l,l.exports,n),l.exports}n.m=i,e=[],n.O=(i,t,a,l)=>{if(!t){var r=1/0;for(d=0;d=l)&&Object.keys(n.O).every((e=>n.O[e](t[s])))?t.splice(s--,1):(o=!1,l0&&e[d-1][2]>l;d--)e[d]=e[d-1];e[d]=[t,a,l]},n.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return n.d(i,{a:i}),i},n.d=(e,i)=>{for(var t in i)n.o(i,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},n.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={500:0,860:0};n.O.j=i=>0===e[i];var i=(i,t)=>{var a,l,[r,o,s]=t,c=0;if(r.some((i=>0!==e[i]))){for(a in o)n.o(o,a)&&(n.m[a]=o[a]);if(s)var d=s(n)}for(i&&i(t);cn(219)));a=n.O(a)})();
\ No newline at end of file
+(()=>{"use strict";var e,n={507:()=>{const e=window.wp.blocks,n=window.wp.element,i=window.wp.blockEditor,a=window.wp.components,t=window.wp.i18n,s=window.ReactJSXRuntime,l=[{label:(0,t.__)("Standaard volgorde","owc-mijn-services"),value:""},{label:(0,t.__)("Startdatum","owc-mijn-services"),value:"startdatum"},{label:(0,t.__)("Einddatum","owc-mijn-services"),value:"einddatum"},{label:(0,t.__)("Publicatiedatum","owc-mijn-services"),value:"publicatiedatum"},{label:(0,t.__)("Archiefactiedatum","owc-mijn-services"),value:"archiefactiedatum"},{label:(0,t.__)("Registratiedatum","owc-mijn-services"),value:"registratiedatum"},{label:(0,t.__)("Zaaknummer","owc-mijn-services"),value:"identificatie"}],r=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"owc-my-services/mijn-zaken","version":"1.0.0","title":"OWC Mijn Zaken","description":"Toont Mijn Zaken blok.","attributes":{"zaakClient":{"type":"string","default":""},"zaakClients":{"type":"array","default":[]},"zaaktypen":{"type":"object","default":{}},"byBSN":{"type":"boolean","default":true},"byKVK":{"type":"boolean","default":false},"showTabs":{"type":"boolean","default":true},"perPage":{"type":"number","default":10},"orderBy":{"type":"string","default":"startdatum"},"orderByDirection":{"type":"string","default":"-"}},"supports":{"html":false},"textdomain":"owc-my-services","editorScript":"file:./index.js","editorStyle":"file:./editor.css","style":"file:./style-index.css","viewScript":"file:./view.js"}');(0,e.registerBlockType)(r,{edit:({attributes:e,setAttributes:r})=>{const{zaakClient:o,zaakClients:c,zaaktypen:d,byBSN:p,byKVK:m,showTabs:u,perPage:v,orderBy:k,orderByDirection:w}=e,g=(window?.owcMyServices?.zaakClientOptions??[]).filter((e=>""!==e.value)),j=window?.owcMyServices?.productionChecksEnabled??!0,h=window?.owcMyServices?.disableKVKFiltering??!1,_=j&&!p&&!(m&&!h),b=window?.owcMyServices?.zaaktypeOptions??{},y=c.filter((e=>(b[e]??[]).length>0));return(0,n.useEffect)((()=>{o&&0===c.length&&r({zaakClients:[o],zaakClient:""})}),[]),(0,n.useEffect)((()=>{const e=window?.owcMyServices?.zaaktypeUrlMigrations??{};if(0===Object.keys(e).length)return;let n=!1;const i={};Object.keys(d).forEach((a=>{const t=e[a]??{},s=(d[a]??[]).map((e=>t[e]?(n=!0,t[e]):e));i[a]=[...new Set(s)]})),n&&r({zaaktypen:i})}),[]),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(i.InspectorControls,{children:[(0,s.jsx)(a.PanelBody,{title:(0,t.__)("Zaaksystemen","owc-mijn-services"),initialOpen:!0,children:(0,s.jsx)(a.__experimentalVStack,{children:0===g.length?(0,s.jsx)(a.Notice,{status:"warning",isDismissible:!1,children:(0,t.__)("Er zijn nog geen zaaksystemen geconfigureerd in de ZGW API-instellingen.","owc-mijn-services")}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("p",{children:(0,t.__)("Uit welke zaaksystemen worden zaken opgehaald?","owc-mijn-services")}),(0,s.jsx)(a.__experimentalVStack,{spacing:2,children:g.map((e=>(0,s.jsx)(a.CheckboxControl,{label:e.label,checked:c.includes(e.value),onChange:n=>((e,n)=>r({zaakClients:n?[...c,e]:c.filter((n=>n!==e))}))(e.value,n)},e.value)))}),0===c.length&&(0,s.jsx)(a.Notice,{status:"warning",isDismissible:!1,children:(0,t.__)("Selecteer minimaal één zaaksysteem. Zonder selectie worden er geen zaken getoond.","owc-mijn-services")})]})})}),c.length>0&&(0,s.jsx)(a.PanelBody,{title:(0,t.__)("Zaaktypen","owc-mijn-services"),initialOpen:!1,children:(0,s.jsx)(a.__experimentalVStack,{children:0===y.length?(0,s.jsx)(a.Notice,{status:"info",isDismissible:!1,children:(0,t.__)("Zaaktypefiltering is uitgeschakeld voor de geselecteerde zaaksystemen. Schakel dit in bij Instellingen → OWC Mijn Services → 'Zaaktypefiltering ondersteunde leveranciers' om hier zaaktypen te kunnen selecteren.","owc-mijn-services")}):(0,s.jsx)(a.BaseControl,{id:"owc-my-services-zaaktypen-selection",label:(0,t.__)("Zaaktypen","owc-mijn-services"),help:(0,t.__)("Selecteer per zaaksysteem de zaaktypen waarop gefilterd moet worden. Selecteer geen zaaktypen om alle zaaktypen te tonen.","owc-mijn-services"),children:y.map((e=>{const n=b[e]??[];return(0,s.jsx)(a.PanelBody,{title:e,initialOpen:!1,className:"owc-my-services-zaaktypen-supplier-panel",children:n.map((n=>(0,s.jsx)(a.CheckboxControl,{label:n.label,checked:(d[e]??[]).includes(n.value),onChange:i=>((e,n,i)=>{const a=d[e]??[],t=i?[...a,n]:a.filter((e=>e!==n));r({zaaktypen:{...d,[e]:t}})})(e,n.value,i)},n.value)))},e)}))})})}),(0,s.jsx)(a.PanelBody,{title:(0,t.__)("Zichtbare zaken","owc-mijn-services"),initialOpen:!0,children:(0,s.jsxs)(a.__experimentalVStack,{children:[(0,s.jsx)(a.ToggleControl,{label:(0,t.__)("Persoonlijke zaken (DigiD)","owc-mijn-services"),help:(0,t.__)("Zaken gekoppeld aan het BSN van de ingelogde gebruiker.","owc-mijn-services"),checked:p,onChange:e=>r({byBSN:e})}),_&&(0,s.jsx)(a.Notice,{status:"warning",isDismissible:!1,children:h?(0,t.__)('Zet "Persoonlijke zaken" aan, anders worden er geen zaken getoond.',"owc-mijn-services"):(0,t.__)("Zet minimaal één van beide opties aan, anders worden er geen zaken getoond.","owc-mijn-services")}),h?(0,s.jsx)(a.Notice,{status:"info",isDismissible:!1,children:(0,t.__)("Zakelijke zaken (eHerkenning) zijn uitgeschakeld in de plugin-instellingen.","owc-mijn-services")}):(0,s.jsx)(a.ToggleControl,{label:(0,t.__)("Zakelijke zaken (eHerkenning)","owc-mijn-services"),help:(0,t.__)("Zaken gekoppeld aan het KVK-nummer van de ingelogde gebruiker.","owc-mijn-services"),checked:m,onChange:e=>r({byKVK:e})})]})}),(0,s.jsx)(a.PanelBody,{title:(0,t.__)("Weergave","owc-mijn-services"),initialOpen:!0,children:(0,s.jsxs)(a.__experimentalVStack,{children:[(0,s.jsx)(a.ToggleControl,{label:(0,t.__)("Tabbladen tonen","owc-mijn-services"),checked:u,onChange:e=>r({showTabs:e})}),(0,s.jsx)(a.RangeControl,{label:(0,t.__)("Aantal zaken","owc-mijn-services"),value:v,min:1,max:25,onChange:e=>r({perPage:e})}),(0,s.jsx)(a.SelectControl,{label:(0,t.__)("Sorteren op","owc-mijn-services"),value:k,options:l,onChange:e=>r({orderBy:e})}),k&&(0,s.jsx)(a.SelectControl,{label:(0,t.__)("Volgorde","owc-mijn-services"),value:w,options:[{label:(0,t.__)("Nieuwste eerst","owc-mijn-services"),value:"-"},{label:(0,t.__)("Oudste eerst","owc-mijn-services"),value:"+"}],onChange:e=>r({orderByDirection:e})})]})})]}),(0,s.jsx)("div",{...(0,i.useBlockProps)(),children:(0,s.jsx)(a.Placeholder,{icon:"list-view",label:(0,t.__)("Het persoonlijke zakenoverzicht","owc-mijn-services"),instructions:(0,t.__)("Toont een overzicht van de zaken voor de ingelogde gebruiker.","owc-mijn-services")})})]})},save:function(){return null}})}},i={};function a(e){var t=i[e];if(void 0!==t)return t.exports;var s=i[e]={exports:{}};return n[e](s,s.exports,a),s.exports}a.m=n,e=[],a.O=(n,i,t,s)=>{if(!i){var l=1/0;for(d=0;d=s)&&Object.keys(a.O).every((e=>a.O[e](i[o])))?i.splice(o--,1):(r=!1,s0&&e[d-1][2]>s;d--)e[d]=e[d-1];e[d]=[i,t,s]},a.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={500:0,860:0};a.O.j=n=>0===e[n];var n=(n,i)=>{var t,s,[l,r,o]=i,c=0;if(l.some((n=>0!==e[n]))){for(t in r)a.o(r,t)&&(a.m[t]=r[t]);if(o)var d=o(a)}for(n&&n(i);ca(507)));t=a.O(t)})();
\ No newline at end of file
diff --git a/build/mijn-zaken/view.asset.php b/build/mijn-zaken/view.asset.php
index bf814fd..9acfa65 100644
--- a/build/mijn-zaken/view.asset.php
+++ b/build/mijn-zaken/view.asset.php
@@ -1 +1 @@
- array('react', 'react-jsx-runtime', 'wp-element'), 'version' => '9159b150c16982566ba2');
+ array('react', 'react-jsx-runtime', 'wp-element'), 'version' => '6b349baaee13d707bf7f');
diff --git a/build/mijn-zaken/view.js b/build/mijn-zaken/view.js
index cf80de2..a99c582 100644
--- a/build/mijn-zaken/view.js
+++ b/build/mijn-zaken/view.js
@@ -1 +1 @@
-(()=>{"use strict";var a={n:e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return a.d(n,{a:n}),n},d:(e,n)=>{for(var r in n)a.o(n,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},o:(a,e)=>Object.prototype.hasOwnProperty.call(a,e)};const e=window.wp.element,n=window.React;var r=a.n(n);function t(a){var e,n,r="";if("string"==typeof a||"number"==typeof a)r+=a;else if("object"==typeof a)if(Array.isArray(a)){var o=a.length;for(e=0;e= 16");if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,e){if((n=n||0)<0||n+16>e.length)throw new RangeError(`UUID byte range ${n}:${n+15} is out of buffer bounds`);for(let a=0;a<16;++a)e[n+a]=r[a];return e}return function(a,e=0){return(l[a[e+0]]+l[a[e+1]]+l[a[e+2]]+l[a[e+3]]+"-"+l[a[e+4]]+l[a[e+5]]+"-"+l[a[e+6]]+l[a[e+7]]+"-"+l[a[e+8]]+l[a[e+9]]+"-"+l[a[e+10]]+l[a[e+11]]+l[a[e+12]]+l[a[e+13]]+l[a[e+14]]+l[a[e+15]]).toLowerCase()}(r)};var h=function(){return h=Object.assign||function(a){for(var e,n=1,r=arguments.length;n0?i[0].tabRef:void 0})),p=g[0],f=g[1],m=(0,n.useState)(),k=m[0],x=m[1],O=(0,n.useState)(),E=O[0],N=O[1];(0,n.useEffect)((function(){l(i.map((function(a,e){return h(h({},a),{tab:h({},o[e])})})))}),[o]),(0,n.useEffect)((function(){var a;void 0!==k&&(null===(a=null==k?void 0:k.current)||void 0===a||a.focus())}),[k]),(0,n.useEffect)((function(){var a,e;void 0!==p&&N({left:null===(a=p.current)||void 0===a?void 0:a.offsetLeft,width:null===(e=p.current)||void 0===e?void 0:e.offsetWidth})}),[p]);var j={"--_denhaag-tabs-tab-indicator-inset-inline-start":"".concat(null!==(e=null==E?void 0:E.left)&&void 0!==e?e:0,"px"),"--_denhaag-tabs-tab-indicator-size":"".concat(null!==(t=null==E?void 0:E.width)&&void 0!==t?t:0,"px")};return r().createElement(r().Fragment,null,r().createElement(b,null,r().createElement(v,{tabIndex:-1,onKeyDown:function(a){"ArrowRight"===a.key?(a.preventDefault(),x((function(a){var e,n,r,t=(n=function(e){return e.tabRef===a},r=(e=i).findIndex(n),e[r===e.length-1?0:r+1]);return null==t?void 0:t.tabRef}))):"ArrowLeft"===a.key?(a.preventDefault(),x((function(a){var e,n,r,t=(n=function(e){return e.tabRef===a},r=(e=i).findIndex(n),e[0===r?e.length-1:r-1]);return null==t?void 0:t.tabRef}))):"Enter"===a.key&&f(k)}},i.map((function(a){var e=a.tab,n=a.tabRef,t=a.tabId,o=a.tabPanelId;return r().createElement(y,{id:t,key:t,"aria-controls":o,tabIndex:p===n?0:-1,ref:n,selected:p===n,onClick:function(){return function(a){f(a),x(a),null==d||d(i.findIndex((function(e){return e.tabRef===a})))}(n)},onFocus:function(){return function(a){x(a)}(n)}},r().createElement(_,null,e.label))}))),r().createElement(u,{style:j})),i.map((function(a){var e=a.tab,n=a.tabRef,t=a.tabId,o=a.tabPanelId;return r().createElement(w,{id:o,key:o,"aria-labelledby":t,tabIndex:0,selected:p===n,hidden:p!==n},e.panelContent)})))};const O=window.ReactJSXRuntime,E=a=>(0,O.jsx)(x,{...a});var N=".denhaag-link{color:var(--denhaag-link-color);cursor:var(--denhaag-link-cursor,pointer);font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding-inline-end:var(--denhaag-link-padding-inline-end,0);padding-inline-start:var(--denhaag-link-padding-inline-start,0);position:relative;text-decoration:var(--denhaag-link-text-decoration)}.denhaag-link--with-icon{--nl-link-text-decoration-line:none;align-items:center;display:inline-flex;gap:var(--denhaag-link-icon-gap);text-decoration:none;vertical-align:var(--denhaag-link-with-icon-vertical-align,baseline)}.denhaag-link--with-small-icon{--denhaag-link-icon-font-size:var(--denhaag-link-small-icon-font-size);--denhaag-link-icon-size:var(--denhaag-link-small-icon-size)}.denhaag-link--with-icon-start{--denhaag-link-icon-order:0;--denhaag-link-padding-inline-start:0}.denhaag-link--external,.denhaag-link--focus,.denhaag-link--hover,.denhaag-link:focus-visible,.denhaag-link:hover{text-decoration:var(--denhaag-link-text-decoration)}.denhaag-link--with-icon-end{--denhaag-link-icon-order:2;--denhaag-link-padding-inline-end:0}.denhaag-link--external{--denhaag-link-icon-align:var(--denhaag-link-external-icon-align)}.denhaag-link--focus,.denhaag-link:focus-visible{--denhaag-link-color:var(--denhaag-link-focus-color);outline:var(--denhaag-link-focus-outline)}.denhaag-link--hover,.denhaag-link:hover{--denhaag-link-color:var(--denhaag-link-hover-color);--denhaag-link-cursor:pointer}.denhaag-link--disabled,.denhaag-link--disabled--hover,.denhaag-link--disabled:hover{--denhaag-link-color:var(--denhaag-link-disabled-color);--denhaag-link-cursor:none;pointer-events:none}.denhaag-link__icon{align-items:var(--denhaag-link-icon-align);display:inline-flex;justify-content:center;order:var(--denhaag-link-icon-order,0);position:relative;vertical-align:text-top}.denhaag-link__icon,.denhaag-link__icon>:first-child{height:var(--denhaag-link-icon-size);width:var(--denhaag-link-icon-size)}.denhaag-link__icon>:first-child{aspect-ratio:1/1;font-size:var(--denhaag-link-icon-font-size);left:50%;position:absolute;top:50%;transform:translate(-50%,-50%)}.denhaag-link__sr-only{clip:rect(0,0,0,0);border:0;height:1px;margin-block-end:-1px;margin-block-start:-1px;margin-inline-end:-1px;margin-inline-start:-1px;overflow:hidden;padding-block-end:0;padding-block-start:0;padding-inline-end:0;padding-inline-start:0;position:absolute;width:1px}";if("undefined"!=typeof document){const a=document.head||document.getElementsByTagName("head")[0],e=document.createElement("style");e.type="text/css",e.nonce=window.NONCE,a.appendChild(e),e.styleSheet?e.styleSheet.cssText=N:e.appendChild(document.createTextNode(N))}var j=function(){return j=Object.assign||function(a){for(var e,n=1,r=arguments.length;n(0,O.jsx)(sa,{...a});document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll(".js-nlds-denhaag-tab-component").forEach((a=>{const n=JSON.parse(a.dataset.tabs).map((a=>({label:a.label,panelContent:a.cards?(0,O.jsx)("div",{className:"denhaag-card-group",children:a.cards.map(((a,e)=>(0,O.jsx)(ha,{...a},e)))}):(0,O.jsx)("div",{dangerouslySetInnerHTML:{__html:a.html||""}})})));(0,e.createRoot)(a).render((0,O.jsx)(E,{tabData:n}))}))}))})();
\ No newline at end of file
+(()=>{"use strict";var a={n:e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return a.d(n,{a:n}),n},d:(e,n)=>{for(var r in n)a.o(n,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},o:(a,e)=>Object.prototype.hasOwnProperty.call(a,e)};const e=window.wp.element,n=window.React;var r=a.n(n);function t(a){var e,n,r="";if("string"==typeof a||"number"==typeof a)r+=a;else if("object"==typeof a)if(Array.isArray(a)){var o=a.length;for(e=0;e= 16");if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,e){if((n=n||0)<0||n+16>e.length)throw new RangeError(`UUID byte range ${n}:${n+15} is out of buffer bounds`);for(let a=0;a<16;++a)e[n+a]=r[a];return e}return function(a,e=0){return(l[a[e+0]]+l[a[e+1]]+l[a[e+2]]+l[a[e+3]]+"-"+l[a[e+4]]+l[a[e+5]]+"-"+l[a[e+6]]+l[a[e+7]]+"-"+l[a[e+8]]+l[a[e+9]]+"-"+l[a[e+10]]+l[a[e+11]]+l[a[e+12]]+l[a[e+13]]+l[a[e+14]]+l[a[e+15]]).toLowerCase()}(r)};var g=function(){return g=Object.assign||function(a){for(var e,n=1,r=arguments.length;n0?i[0].tabRef:void 0})),p=h[0],f=h[1],m=(0,n.useState)(),k=m[0],x=m[1],O=(0,n.useState)(),E=O[0],N=O[1];(0,n.useEffect)((function(){l(i.map((function(a,e){return g(g({},a),{tab:g({},o[e])})})))}),[o]),(0,n.useEffect)((function(){var a;void 0!==k&&(null===(a=null==k?void 0:k.current)||void 0===a||a.focus())}),[k]),(0,n.useEffect)((function(){var a,e;void 0!==p&&N({left:null===(a=p.current)||void 0===a?void 0:a.offsetLeft,width:null===(e=p.current)||void 0===e?void 0:e.offsetWidth})}),[p]);var j={"--_denhaag-tabs-tab-indicator-inset-inline-start":"".concat(null!==(e=null==E?void 0:E.left)&&void 0!==e?e:0,"px"),"--_denhaag-tabs-tab-indicator-size":"".concat(null!==(t=null==E?void 0:E.width)&&void 0!==t?t:0,"px")};return r().createElement(r().Fragment,null,r().createElement(b,null,r().createElement(v,{tabIndex:-1,onKeyDown:function(a){"ArrowRight"===a.key?(a.preventDefault(),x((function(a){var e,n,r,t=(n=function(e){return e.tabRef===a},r=(e=i).findIndex(n),e[r===e.length-1?0:r+1]);return null==t?void 0:t.tabRef}))):"ArrowLeft"===a.key?(a.preventDefault(),x((function(a){var e,n,r,t=(n=function(e){return e.tabRef===a},r=(e=i).findIndex(n),e[0===r?e.length-1:r-1]);return null==t?void 0:t.tabRef}))):"Enter"===a.key&&f(k)}},i.map((function(a){var e=a.tab,n=a.tabRef,t=a.tabId,o=a.tabPanelId;return r().createElement(y,{id:t,key:t,"aria-controls":o,tabIndex:p===n?0:-1,ref:n,selected:p===n,onClick:function(){return function(a){f(a),x(a),null==d||d(i.findIndex((function(e){return e.tabRef===a})))}(n)},onFocus:function(){return function(a){x(a)}(n)}},r().createElement(_,null,e.label))}))),r().createElement(u,{style:j})),i.map((function(a){var e=a.tab,n=a.tabRef,t=a.tabId,o=a.tabPanelId;return r().createElement(w,{id:o,key:o,"aria-labelledby":t,tabIndex:0,selected:p===n,hidden:p!==n},e.panelContent)})))};const O=window.ReactJSXRuntime,E=a=>(0,O.jsx)(x,{...a});var N=".denhaag-link{color:var(--denhaag-link-color);cursor:var(--denhaag-link-cursor,pointer);font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding-inline-end:var(--denhaag-link-padding-inline-end,0);padding-inline-start:var(--denhaag-link-padding-inline-start,0);position:relative;text-decoration:var(--denhaag-link-text-decoration)}.denhaag-link--with-icon{--nl-link-text-decoration-line:none;align-items:center;display:inline-flex;gap:var(--denhaag-link-icon-gap);text-decoration:none;vertical-align:var(--denhaag-link-with-icon-vertical-align,baseline)}.denhaag-link--with-small-icon{--denhaag-link-icon-font-size:var(--denhaag-link-small-icon-font-size);--denhaag-link-icon-size:var(--denhaag-link-small-icon-size)}.denhaag-link--with-icon-start{--denhaag-link-icon-order:0;--denhaag-link-padding-inline-start:0}.denhaag-link--external,.denhaag-link--focus,.denhaag-link--hover,.denhaag-link:focus-visible,.denhaag-link:hover{text-decoration:var(--denhaag-link-text-decoration)}.denhaag-link--with-icon-end{--denhaag-link-icon-order:2;--denhaag-link-padding-inline-end:0}.denhaag-link--external{--denhaag-link-icon-align:var(--denhaag-link-external-icon-align)}.denhaag-link--focus,.denhaag-link:focus-visible{--denhaag-link-color:var(--denhaag-link-focus-color);outline:var(--denhaag-link-focus-outline)}.denhaag-link--hover,.denhaag-link:hover{--denhaag-link-color:var(--denhaag-link-hover-color);--denhaag-link-cursor:pointer}.denhaag-link--disabled,.denhaag-link--disabled--hover,.denhaag-link--disabled:hover{--denhaag-link-color:var(--denhaag-link-disabled-color);--denhaag-link-cursor:none;pointer-events:none}.denhaag-link__icon{align-items:var(--denhaag-link-icon-align);display:inline-flex;justify-content:center;order:var(--denhaag-link-icon-order,0);position:relative;vertical-align:text-top}.denhaag-link__icon,.denhaag-link__icon>:first-child{height:var(--denhaag-link-icon-size);width:var(--denhaag-link-icon-size)}.denhaag-link__icon>:first-child{aspect-ratio:1/1;font-size:var(--denhaag-link-icon-font-size);left:50%;position:absolute;top:50%;transform:translate(-50%,-50%)}.denhaag-link__sr-only{clip:rect(0,0,0,0);border:0;height:1px;margin-block-end:-1px;margin-block-start:-1px;margin-inline-end:-1px;margin-inline-start:-1px;overflow:hidden;padding-block-end:0;padding-block-start:0;padding-inline-end:0;padding-inline-start:0;position:absolute;width:1px}";if("undefined"!=typeof document){const a=document.head||document.getElementsByTagName("head")[0],e=document.createElement("style");e.type="text/css",e.nonce=window.NONCE,a.appendChild(e),e.styleSheet?e.styleSheet.cssText=N:e.appendChild(document.createTextNode(N))}var j=function(){return j=Object.assign||function(a){for(var e,n=1,r=arguments.length;n(0,O.jsx)(sa,{...a}),ha=({cards:a})=>(0,O.jsx)("div",{className:"denhaag-card-group",children:a.map(((a,e)=>(0,O.jsx)(ga,{...a},e)))}),pa=({message:a})=>(0,O.jsx)("p",{className:"nl-paragraph owc-zaken-empty",children:a}),ba=a=>a.cards?.length?(0,O.jsx)(ha,{cards:a.cards}):a.html?(0,O.jsx)("div",{dangerouslySetInnerHTML:{__html:a.html}}):(0,O.jsx)(pa,{message:a.emptyMessage});document.addEventListener("DOMContentLoaded",(()=>{document.querySelectorAll(".js-nlds-denhaag-tab-component").forEach((a=>{const n=JSON.parse(a.dataset.tabsEnabled??"true"),r=(0,e.createRoot)(a);if(!n){const e=JSON.parse(a.dataset.cards??"[]");return void r.render((0,O.jsx)(ha,{cards:e}))}const t=JSON.parse(a.dataset.tabs).map((a=>({label:a.label,panelContent:ba(a)})));r.render((0,O.jsx)(E,{tabData:t}))}))}))})();
\ No newline at end of file
diff --git a/build/select2/LICENSE.md b/build/select2/LICENSE.md
new file mode 100644
index 0000000..8cb8a2b
--- /dev/null
+++ b/build/select2/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/build/select2/select2.min.css b/build/select2/select2.min.css
new file mode 100644
index 0000000..7c18ad5
--- /dev/null
+++ b/build/select2/select2.min.css
@@ -0,0 +1 @@
+.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;padding:1px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
diff --git a/build/select2/select2.min.js b/build/select2/select2.min.js
new file mode 100644
index 0000000..091d4b5
--- /dev/null
+++ b/build/select2/select2.min.js
@@ -0,0 +1,2 @@
+/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
+!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(u){var e=function(){if(u&&u.fn&&u.fn.select2&&u.fn.select2.amd)var e=u.fn.select2.amd;var t,n,r,h,o,s,f,g,m,v,y,_,i,a,b;function w(e,t){return i.call(e,t)}function l(e,t){var n,r,i,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"]||{};if(e){for(s=(e=e.split("/")).length-1,y.nodeIdCompat&&b.test(e[s])&&(e[s]=e[s].replace(b,"")),"."===e[0].charAt(0)&&h&&(e=h.slice(0,h.length-1).concat(e)),u=0;u":">",'"':""","'":"'","/":"/"};return"string"!=typeof e?e:String(e).replace(/[&<>"'\/\\]/g,function(e){return t[e]})},i.appendMany=function(e,t){if("1.7"===o.fn.jquery.substr(0,3)){var n=o();o.map(t,function(e){n=n.add(e)}),t=n}e.append(t)},i.__cache={};var n=0;return i.GetUniqueElementId=function(e){var t=e.getAttribute("data-select2-id");return null==t&&(e.id?(t=e.id,e.setAttribute("data-select2-id",t)):(e.setAttribute("data-select2-id",++n),t=n.toString())),t},i.StoreData=function(e,t,n){var r=i.GetUniqueElementId(e);i.__cache[r]||(i.__cache[r]={}),i.__cache[r][t]=n},i.GetData=function(e,t){var n=i.GetUniqueElementId(e);return t?i.__cache[n]&&null!=i.__cache[n][t]?i.__cache[n][t]:o(e).data(t):i.__cache[n]},i.RemoveData=function(e){var t=i.GetUniqueElementId(e);null!=i.__cache[t]&&delete i.__cache[t],e.removeAttribute("data-select2-id")},i}),e.define("select2/results",["jquery","./utils"],function(h,f){function r(e,t,n){this.$element=e,this.data=n,this.options=t,r.__super__.constructor.call(this)}return f.Extend(r,f.Observable),r.prototype.render=function(){var e=h('');return this.options.get("multiple")&&e.attr("aria-multiselectable","true"),this.$results=e},r.prototype.clear=function(){this.$results.empty()},r.prototype.displayMessage=function(e){var t=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var n=h(' '),r=this.options.get("translations").get(e.message);n.append(t(r(e.args))),n[0].className+=" select2-results__message",this.$results.append(n)},r.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},r.prototype.append=function(e){this.hideLoading();var t=[];if(null!=e.results&&0!==e.results.length){e.results=this.sort(e.results);for(var n=0;n",{class:"select2-results__options select2-results__options--nested"});p.append(l),s.append(a),s.append(p)}else this.template(e,t);return f.StoreData(t,"data",e),t},r.prototype.bind=function(t,e){var l=this,n=t.id+"-results";this.$results.attr("id",n),t.on("results:all",function(e){l.clear(),l.append(e.data),t.isOpen()&&(l.setClasses(),l.highlightFirstItem())}),t.on("results:append",function(e){l.append(e.data),t.isOpen()&&l.setClasses()}),t.on("query",function(e){l.hideMessages(),l.showLoading(e)}),t.on("select",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("unselect",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("open",function(){l.$results.attr("aria-expanded","true"),l.$results.attr("aria-hidden","false"),l.setClasses(),l.ensureHighlightVisible()}),t.on("close",function(){l.$results.attr("aria-expanded","false"),l.$results.attr("aria-hidden","true"),l.$results.removeAttr("aria-activedescendant")}),t.on("results:toggle",function(){var e=l.getHighlightedResults();0!==e.length&&e.trigger("mouseup")}),t.on("results:select",function(){var e=l.getHighlightedResults();if(0!==e.length){var t=f.GetData(e[0],"data");"true"==e.attr("aria-selected")?l.trigger("close",{}):l.trigger("select",{data:t})}}),t.on("results:previous",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e);if(!(n<=0)){var r=n-1;0===e.length&&(r=0);var i=t.eq(r);i.trigger("mouseenter");var o=l.$results.offset().top,s=i.offset().top,a=l.$results.scrollTop()+(s-o);0===r?l.$results.scrollTop(0):s-o<0&&l.$results.scrollTop(a)}}),t.on("results:next",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e)+1;if(!(n>=t.length)){var r=t.eq(n);r.trigger("mouseenter");var i=l.$results.offset().top+l.$results.outerHeight(!1),o=r.offset().top+r.outerHeight(!1),s=l.$results.scrollTop()+o-i;0===n?l.$results.scrollTop(0):ithis.$results.outerHeight()||o<0)&&this.$results.scrollTop(i)}},r.prototype.template=function(e,t){var n=this.options.get("templateResult"),r=this.options.get("escapeMarkup"),i=n(e,t);null==i?t.style.display="none":"string"==typeof i?t.innerHTML=r(i):h(t).append(i)},r}),e.define("select2/keys",[],function(){return{BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46}}),e.define("select2/selection/base",["jquery","../utils","../keys"],function(n,r,i){function o(e,t){this.$element=e,this.options=t,o.__super__.constructor.call(this)}return r.Extend(o,r.Observable),o.prototype.render=function(){var e=n(' ');return this._tabindex=0,null!=r.GetData(this.$element[0],"old-tabindex")?this._tabindex=r.GetData(this.$element[0],"old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),e.attr("title",this.$element.attr("title")),e.attr("tabindex",this._tabindex),e.attr("aria-disabled","false"),this.$selection=e},o.prototype.bind=function(e,t){var n=this,r=e.id+"-results";this.container=e,this.$selection.on("focus",function(e){n.trigger("focus",e)}),this.$selection.on("blur",function(e){n._handleBlur(e)}),this.$selection.on("keydown",function(e){n.trigger("keypress",e),e.which===i.SPACE&&e.preventDefault()}),e.on("results:focus",function(e){n.$selection.attr("aria-activedescendant",e.data._resultId)}),e.on("selection:update",function(e){n.update(e.data)}),e.on("open",function(){n.$selection.attr("aria-expanded","true"),n.$selection.attr("aria-owns",r),n._attachCloseHandler(e)}),e.on("close",function(){n.$selection.attr("aria-expanded","false"),n.$selection.removeAttr("aria-activedescendant"),n.$selection.removeAttr("aria-owns"),n.$selection.trigger("focus"),n._detachCloseHandler(e)}),e.on("enable",function(){n.$selection.attr("tabindex",n._tabindex),n.$selection.attr("aria-disabled","false")}),e.on("disable",function(){n.$selection.attr("tabindex","-1"),n.$selection.attr("aria-disabled","true")})},o.prototype._handleBlur=function(e){var t=this;window.setTimeout(function(){document.activeElement==t.$selection[0]||n.contains(t.$selection[0],document.activeElement)||t.trigger("blur",e)},1)},o.prototype._attachCloseHandler=function(e){n(document.body).on("mousedown.select2."+e.id,function(e){var t=n(e.target).closest(".select2");n(".select2.select2-container--open").each(function(){this!=t[0]&&r.GetData(this,"element").select2("close")})})},o.prototype._detachCloseHandler=function(e){n(document.body).off("mousedown.select2."+e.id)},o.prototype.position=function(e,t){t.find(".selection").append(e)},o.prototype.destroy=function(){this._detachCloseHandler(this.container)},o.prototype.update=function(e){throw new Error("The `update` method must be defined in child classes.")},o.prototype.isEnabled=function(){return!this.isDisabled()},o.prototype.isDisabled=function(){return this.options.get("disabled")},o}),e.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(e,t,n,r){function i(){i.__super__.constructor.apply(this,arguments)}return n.Extend(i,t),i.prototype.render=function(){var e=i.__super__.render.call(this);return e.addClass("select2-selection--single"),e.html(' '),e},i.prototype.bind=function(t,e){var n=this;i.__super__.bind.apply(this,arguments);var r=t.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",r).attr("role","textbox").attr("aria-readonly","true"),this.$selection.attr("aria-labelledby",r),this.$selection.on("mousedown",function(e){1===e.which&&n.trigger("toggle",{originalEvent:e})}),this.$selection.on("focus",function(e){}),this.$selection.on("blur",function(e){}),t.on("focus",function(e){t.isOpen()||n.$selection.trigger("focus")})},i.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},i.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},i.prototype.selectionContainer=function(){return e(" ")},i.prototype.update=function(e){if(0!==e.length){var t=e[0],n=this.$selection.find(".select2-selection__rendered"),r=this.display(t,n);n.empty().append(r);var i=t.title||t.text;i?n.attr("title",i):n.removeAttr("title")}else this.clear()},i}),e.define("select2/selection/multiple",["jquery","./base","../utils"],function(i,e,l){function n(e,t){n.__super__.constructor.apply(this,arguments)}return l.Extend(n,e),n.prototype.render=function(){var e=n.__super__.render.call(this);return e.addClass("select2-selection--multiple"),e.html(''),e},n.prototype.bind=function(e,t){var r=this;n.__super__.bind.apply(this,arguments),this.$selection.on("click",function(e){r.trigger("toggle",{originalEvent:e})}),this.$selection.on("click",".select2-selection__choice__remove",function(e){if(!r.isDisabled()){var t=i(this).parent(),n=l.GetData(t[0],"data");r.trigger("unselect",{originalEvent:e,data:n})}})},n.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},n.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},n.prototype.selectionContainer=function(){return i('× ')},n.prototype.update=function(e){if(this.clear(),0!==e.length){for(var t=[],n=0;n×');a.StoreData(r[0],"data",t),this.$selection.find(".select2-selection__rendered").prepend(r)}},e}),e.define("select2/selection/search",["jquery","../utils","../keys"],function(r,a,l){function e(e,t,n){e.call(this,t,n)}return e.prototype.render=function(e){var t=r(' ');this.$searchContainer=t,this.$search=t.find("input");var n=e.call(this);return this._transferTabIndex(),n},e.prototype.bind=function(e,t,n){var r=this,i=t.id+"-results";e.call(this,t,n),t.on("open",function(){r.$search.attr("aria-controls",i),r.$search.trigger("focus")}),t.on("close",function(){r.$search.val(""),r.$search.removeAttr("aria-controls"),r.$search.removeAttr("aria-activedescendant"),r.$search.trigger("focus")}),t.on("enable",function(){r.$search.prop("disabled",!1),r._transferTabIndex()}),t.on("disable",function(){r.$search.prop("disabled",!0)}),t.on("focus",function(e){r.$search.trigger("focus")}),t.on("results:focus",function(e){e.data._resultId?r.$search.attr("aria-activedescendant",e.data._resultId):r.$search.removeAttr("aria-activedescendant")}),this.$selection.on("focusin",".select2-search--inline",function(e){r.trigger("focus",e)}),this.$selection.on("focusout",".select2-search--inline",function(e){r._handleBlur(e)}),this.$selection.on("keydown",".select2-search--inline",function(e){if(e.stopPropagation(),r.trigger("keypress",e),r._keyUpPrevented=e.isDefaultPrevented(),e.which===l.BACKSPACE&&""===r.$search.val()){var t=r.$searchContainer.prev(".select2-selection__choice");if(0this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumSelectionLength",[],function(){function e(e,t,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("select",function(){r._checkIfMaximumSelected()})},e.prototype.query=function(e,t,n){var r=this;this._checkIfMaximumSelected(function(){e.call(r,t,n)})},e.prototype._checkIfMaximumSelected=function(e,n){var r=this;this.current(function(e){var t=null!=e?e.length:0;0=r.maximumSelectionLength?r.trigger("results:message",{message:"maximumSelected",args:{maximum:r.maximumSelectionLength}}):n&&n()})},e}),e.define("select2/dropdown",["jquery","./utils"],function(t,e){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t(' ');return e.attr("dir",this.options.get("dir")),this.$dropdown=e},n.prototype.bind=function(){},n.prototype.position=function(e,t){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),e.define("select2/dropdown/search",["jquery","../utils"],function(o,e){function t(){}return t.prototype.render=function(e){var t=e.call(this),n=o(' ');return this.$searchContainer=n,this.$search=n.find("input"),t.prepend(n),t},t.prototype.bind=function(e,t,n){var r=this,i=t.id+"-results";e.call(this,t,n),this.$search.on("keydown",function(e){r.trigger("keypress",e),r._keyUpPrevented=e.isDefaultPrevented()}),this.$search.on("input",function(e){o(this).off("keyup")}),this.$search.on("keyup input",function(e){r.handleSearch(e)}),t.on("open",function(){r.$search.attr("tabindex",0),r.$search.attr("aria-controls",i),r.$search.trigger("focus"),window.setTimeout(function(){r.$search.trigger("focus")},0)}),t.on("close",function(){r.$search.attr("tabindex",-1),r.$search.removeAttr("aria-controls"),r.$search.removeAttr("aria-activedescendant"),r.$search.val(""),r.$search.trigger("blur")}),t.on("focus",function(){t.isOpen()||r.$search.trigger("focus")}),t.on("results:all",function(e){null!=e.query.term&&""!==e.query.term||(r.showSearch(e)?r.$searchContainer.removeClass("select2-search--hide"):r.$searchContainer.addClass("select2-search--hide"))}),t.on("results:focus",function(e){e.data._resultId?r.$search.attr("aria-activedescendant",e.data._resultId):r.$search.removeAttr("aria-activedescendant")})},t.prototype.handleSearch=function(e){if(!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},t.prototype.showSearch=function(e,t){return!0},t}),e.define("select2/dropdown/hidePlaceholder",[],function(){function e(e,t,n,r){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n,r)}return e.prototype.append=function(e,t){t.results=this.removePlaceholder(t.results),e.call(this,t)},e.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},e.prototype.removePlaceholder=function(e,t){for(var n=t.slice(0),r=t.length-1;0<=r;r--){var i=t[r];this.placeholder.id===i.id&&n.splice(r,1)}return n},e}),e.define("select2/dropdown/infiniteScroll",["jquery"],function(n){function e(e,t,n,r){this.lastParams={},e.call(this,t,n,r),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return e.prototype.append=function(e,t){this.$loadingMore.remove(),this.loading=!1,e.call(this,t),this.showLoadingMore(t)&&(this.$results.append(this.$loadingMore),this.loadMoreIfNeeded())},e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("query",function(e){r.lastParams=e,r.loading=!0}),t.on("query:append",function(e){r.lastParams=e,r.loading=!0}),this.$results.on("scroll",this.loadMoreIfNeeded.bind(this))},e.prototype.loadMoreIfNeeded=function(){var e=n.contains(document.documentElement,this.$loadingMore[0]);if(!this.loading&&e){var t=this.$results.offset().top+this.$results.outerHeight(!1);this.$loadingMore.offset().top+this.$loadingMore.outerHeight(!1)<=t+50&&this.loadMore()}},e.prototype.loadMore=function(){this.loading=!0;var e=n.extend({},{page:1},this.lastParams);e.page++,this.trigger("query:append",e)},e.prototype.showLoadingMore=function(e,t){return t.pagination&&t.pagination.more},e.prototype.createLoadingMore=function(){var e=n(' '),t=this.options.get("translations").get("loadingMore");return e.html(t(this.lastParams)),e},e}),e.define("select2/dropdown/attachBody",["jquery","../utils"],function(f,a){function e(e,t,n){this.$dropdownParent=f(n.get("dropdownParent")||document.body),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("open",function(){r._showDropdown(),r._attachPositioningHandler(t),r._bindContainerResultHandlers(t)}),t.on("close",function(){r._hideDropdown(),r._detachPositioningHandler(t)}),this.$dropdownContainer.on("mousedown",function(e){e.stopPropagation()})},e.prototype.destroy=function(e){e.call(this),this.$dropdownContainer.remove()},e.prototype.position=function(e,t,n){t.attr("class",n.attr("class")),t.removeClass("select2"),t.addClass("select2-container--open"),t.css({position:"absolute",top:-999999}),this.$container=n},e.prototype.render=function(e){var t=f(" "),n=e.call(this);return t.append(n),this.$dropdownContainer=t},e.prototype._hideDropdown=function(e){this.$dropdownContainer.detach()},e.prototype._bindContainerResultHandlers=function(e,t){if(!this._containerResultsHandlersBound){var n=this;t.on("results:all",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:append",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:message",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("select",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("unselect",function(){n._positionDropdown(),n._resizeDropdown()}),this._containerResultsHandlersBound=!0}},e.prototype._attachPositioningHandler=function(e,t){var n=this,r="scroll.select2."+t.id,i="resize.select2."+t.id,o="orientationchange.select2."+t.id,s=this.$container.parents().filter(a.hasScroll);s.each(function(){a.StoreData(this,"select2-scroll-position",{x:f(this).scrollLeft(),y:f(this).scrollTop()})}),s.on(r,function(e){var t=a.GetData(this,"select2-scroll-position");f(this).scrollTop(t.y)}),f(window).on(r+" "+i+" "+o,function(e){n._positionDropdown(),n._resizeDropdown()})},e.prototype._detachPositioningHandler=function(e,t){var n="scroll.select2."+t.id,r="resize.select2."+t.id,i="orientationchange.select2."+t.id;this.$container.parents().filter(a.hasScroll).off(n),f(window).off(n+" "+r+" "+i)},e.prototype._positionDropdown=function(){var e=f(window),t=this.$dropdown.hasClass("select2-dropdown--above"),n=this.$dropdown.hasClass("select2-dropdown--below"),r=null,i=this.$container.offset();i.bottom=i.top+this.$container.outerHeight(!1);var o={height:this.$container.outerHeight(!1)};o.top=i.top,o.bottom=i.top+o.height;var s=this.$dropdown.outerHeight(!1),a=e.scrollTop(),l=e.scrollTop()+e.height(),c=ai.bottom+s,d={left:i.left,top:o.bottom},p=this.$dropdownParent;"static"===p.css("position")&&(p=p.offsetParent());var h={top:0,left:0};(f.contains(document.body,p[0])||p[0].isConnected)&&(h=p.offset()),d.top-=h.top,d.left-=h.left,t||n||(r="below"),u||!c||t?!c&&u&&t&&(r="below"):r="above",("above"==r||t&&"below"!==r)&&(d.top=o.top-h.top-s),null!=r&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+r),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+r)),this.$dropdownContainer.css(d)},e.prototype._resizeDropdown=function(){var e={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(e.minWidth=e.width,e.position="relative",e.width="auto"),this.$dropdown.css(e)},e.prototype._showDropdown=function(e){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},e}),e.define("select2/dropdown/minimumResultsForSearch",[],function(){function e(e,t,n,r){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),e.call(this,t,n,r)}return e.prototype.showSearch=function(e,t){return!(function e(t){for(var n=0,r=0;r ');return e.attr("dir",this.options.get("dir")),this.$container=e,this.$container.addClass("select2-container--"+this.options.get("theme")),u.StoreData(e[0],"element",this.$element),e},d}),e.define("jquery-mousewheel",["jquery"],function(e){return e}),e.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults","./select2/utils"],function(i,e,o,t,s){if(null==i.fn.select2){var a=["open","close","destroy"];i.fn.select2=function(t){if("object"==typeof(t=t||{}))return this.each(function(){var e=i.extend(!0,{},t);new o(i(this),e)}),this;if("string"!=typeof t)throw new Error("Invalid arguments for Select2: "+t);var n,r=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=s.GetData(this,"select2");null==e&&window.console&&console.error&&console.error("The select2('"+t+"') method was called on an element that is not using Select2."),n=e[t].apply(e,r)}),-1 array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '27206ca7dc09304c66a0');
+ array('react-jsx-runtime', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'f5a762891a7715127f5c');
diff --git a/build/zaak/index.js b/build/zaak/index.js
index 38f231e..25d1f97 100644
--- a/build/zaak/index.js
+++ b/build/zaak/index.js
@@ -1 +1 @@
-(()=>{"use strict";var e,i={502:(e,i,n)=>{const t=window.wp.blocks,r=window.wp.element,s=window.wp.blockEditor,o=window.wp.components,l=window.wp.serverSideRender;var a=n.n(l);const c=window.wp.i18n,d=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"owc-my-services/zaak","version":"1.0.0","title":"OWC Zaak","description":"Toont Zaak blok.","attributes":{"zaakClient":{"type":"string","default":""},"zaakClients":{"type":"array","default":[]},"byBSN":{"type":"boolean","default":true},"byKVK":{"type":"boolean","default":false}},"supports":{"html":false},"textdomain":"owc-my-services","editorScript":"file:./index.js","editorStyle":"file:./editor.css","style":"file:./style-index.css","viewScript":"file:./view.js"}'),p=window.ReactJSXRuntime;(0,t.registerBlockType)(d,{edit:function({attributes:e,setAttributes:i}){const{zaakClient:n,zaakClients:t,byBSN:l,byKVK:u}=e,w=(window?.owcMyServices?.zaakClientOptions??[]).filter((e=>""!==e.value)),v=(window?.owcMyServices?.productionChecksEnabled??!0)&&!l&&!(u&&!b),b=window?.owcMyServices?.disableKVKFiltering??!1;return(0,r.useEffect)((()=>{n&&0===t.length&&i({zaakClients:[n],zaakClient:""})}),[]),(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(s.InspectorControls,{children:(0,p.jsxs)(o.PanelBody,{title:(0,c.__)("Instellingen","owc-mijn-services"),initialOpen:!0,children:[w.length>0?(0,p.jsx)(o.BaseControl,{id:"owc-my-services-zaak-clients-checkbox-selection",label:(0,c.__)("Zaaksystemen","owc-mijn-services"),help:(0,c.__)("Selecteer de zaaksystemen waarvan zaken op deze pagina getoond kunnen worden.","owc-mijn-services"),children:w.map((e=>(0,p.jsx)(o.CheckboxControl,{label:e.label,checked:t.includes(e.value),onChange:n=>{const r=n?[...t,e.value]:t.filter((i=>i!==e.value));i({zaakClients:r})}},e.value)))}):(0,p.jsx)("p",{children:(0,c.__)("Geen leveranciers geconfigureerd.","owc-mijn-services")}),v&&(0,p.jsx)(o.Notice,{status:"warning",isDismissible:!1,children:b?(0,c.__)("Schakel de filteroptie Filter op BSN in.","owc-mijn-services"):(0,c.__)("Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK.","owc-mijn-services")}),(0,p.jsx)(o.CheckboxControl,{label:"Filter op BSN",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het BSN nummer.",checked:l,onChange:e=>i({byBSN:e})}),!b&&(0,p.jsx)(o.CheckboxControl,{label:"Filter op KVK",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het KVK nummer.",checked:u,onChange:e=>i({byKVK:e})}),b&&(0,p.jsx)(o.Notice,{status:"warning",isDismissible:!1,children:(0,c.__)("Filteren op KVK is uitgeschakeld in de instellingen van de plugin.","owc-mijn-services")})]})}),(0,p.jsx)("div",{...(0,s.useBlockProps)(),children:(0,p.jsx)(o.Disabled,{children:(0,p.jsx)(a(),{block:d.name,attributes:e})})})]})},save:function(){return null}})}},n={};function t(e){var r=n[e];if(void 0!==r)return r.exports;var s=n[e]={exports:{}};return i[e](s,s.exports,t),s.exports}t.m=i,e=[],t.O=(i,n,r,s)=>{if(!n){var o=1/0;for(d=0;d=s)&&Object.keys(t.O).every((e=>t.O[e](n[a])))?n.splice(a--,1):(l=!1,s0&&e[d-1][2]>s;d--)e[d]=e[d-1];e[d]=[n,r,s]},t.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return t.d(i,{a:i}),i},t.d=(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},t.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={613:0,785:0};t.O.j=i=>0===e[i];var i=(i,n)=>{var r,s,[o,l,a]=n,c=0;if(o.some((i=>0!==e[i]))){for(r in l)t.o(l,r)&&(t.m[r]=l[r]);if(a)var d=a(t)}for(i&&i(n);ct(502)));r=t.O(r)})();
\ No newline at end of file
+(()=>{"use strict";var e,i={446:()=>{const e=window.wp.blocks,i=window.wp.element,n=window.wp.blockEditor,s=window.wp.components,t=window.wp.i18n,o=window.ReactJSXRuntime,a=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"owc-my-services/zaak","version":"1.0.0","title":"OWC Zaak","description":"Toont Zaak blok.","attributes":{"zaakClient":{"type":"string","default":""},"zaakClients":{"type":"array","default":[]},"byBSN":{"type":"boolean","default":true},"byKVK":{"type":"boolean","default":false}},"supports":{"html":false},"textdomain":"owc-my-services","editorScript":"file:./index.js","editorStyle":"file:./editor.css","style":"file:./style-index.css","viewScript":"file:./view.js"}');(0,e.registerBlockType)(a,{edit:function({attributes:e,setAttributes:a}){const{zaakClient:l,zaakClients:r,byBSN:c,byKVK:d}=e,p=(window?.owcMyServices?.zaakClientOptions??[]).filter((e=>""!==e.value)),v=window?.owcMyServices?.productionChecksEnabled??!0,w=window?.owcMyServices?.disableKVKFiltering??!1,u=v&&!c&&!(d&&!w);return(0,i.useEffect)((()=>{l&&0===r.length&&a({zaakClients:[l],zaakClient:""})}),[]),(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.InspectorControls,{children:(0,o.jsxs)(s.PanelBody,{title:(0,t.__)("Instellingen","owc-mijn-services"),initialOpen:!0,children:[p.length>0?(0,o.jsx)(s.BaseControl,{id:"owc-my-services-zaak-clients-checkbox-selection",label:(0,t.__)("Zaaksystemen","owc-mijn-services"),help:(0,t.__)("Selecteer de zaaksystemen waarvan zaken op deze pagina getoond kunnen worden.","owc-mijn-services"),children:p.map((e=>(0,o.jsx)(s.CheckboxControl,{label:e.label,checked:r.includes(e.value),onChange:i=>{const n=i?[...r,e.value]:r.filter((i=>i!==e.value));a({zaakClients:n})}},e.value)))}):(0,o.jsx)("p",{children:(0,t.__)("Geen leveranciers geconfigureerd.","owc-mijn-services")}),u&&(0,o.jsx)(s.Notice,{status:"warning",isDismissible:!1,children:w?(0,t.__)("Schakel de filteroptie Filter op BSN in.","owc-mijn-services"):(0,t.__)("Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK.","owc-mijn-services")}),(0,o.jsx)(s.CheckboxControl,{label:"Filter op BSN",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het BSN nummer.",checked:c,onChange:e=>a({byBSN:e})}),!w&&(0,o.jsx)(s.CheckboxControl,{label:"Filter op KVK",help:"Filter zaken die aangemaakt zijn door de ingelogde gebruiker op basis van het KVK nummer.",checked:d,onChange:e=>a({byKVK:e})}),w&&(0,o.jsx)(s.Notice,{status:"warning",isDismissible:!1,children:(0,t.__)("Filteren op KVK is uitgeschakeld in de instellingen van de plugin.","owc-mijn-services")})]})}),(0,o.jsx)("div",{...(0,n.useBlockProps)(),children:(0,o.jsx)(s.Placeholder,{icon:"list-view",label:(0,t.__)("Detailpagina van een zaak","owc-mijn-services"),instructions:(0,t.__)("Toont de details van een specifieke zaak.","owc-mijn-services")})})]})},save:function(){return null}})}},n={};function s(e){var t=n[e];if(void 0!==t)return t.exports;var o=n[e]={exports:{}};return i[e](o,o.exports,s),o.exports}s.m=i,e=[],s.O=(i,n,t,o)=>{if(!n){var a=1/0;for(d=0;d=o)&&Object.keys(s.O).every((e=>s.O[e](n[r])))?n.splice(r--,1):(l=!1,o0&&e[d-1][2]>o;d--)e[d]=e[d-1];e[d]=[n,t,o]},s.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={613:0,785:0};s.O.j=i=>0===e[i];var i=(i,n)=>{var t,o,[a,l,r]=n,c=0;if(a.some((i=>0!==e[i]))){for(t in l)s.o(l,t)&&(s.m[t]=l[t]);if(r)var d=r(s)}for(i&&i(n);c s(446)));t=s.O(t)})();
\ No newline at end of file
diff --git a/config/php-di.php b/config/php-di.php
index 367bfb7..3de1ff9 100644
--- a/config/php-di.php
+++ b/config/php-di.php
@@ -3,6 +3,7 @@
declare(strict_types=1);
use DI\Container;
+use OWC\My_Services\Settings\InformatieobjecttypeCatalog;
use OWC\My_Services\Settings\Settings;
return array(
@@ -89,10 +90,32 @@ function ( $client ) use ( $name ) {
'display.exclude-doc-docx' => function ( Container $container ) {
return (bool) $container->make( 'zgw.settings', array( 'owc-mijn-services-exclude-doc-docx' ) );
},
+ /**
+ * Allowed informatieobjecttypen, grouped per supplier, so filtering only applies to the
+ * supplier(s) for which informatieobjecttypen were actually selected.
+ *
+ * @return array
+ */
'display.allowed-informatieobjecttypen' => function ( Container $container ) {
$value = $container->make( 'zgw.settings', array( 'owc-mijn-services-allowed-informatieobjecttypen' ) );
+ $urls = is_array( $value ) ? array_values( array_filter( $value ) ) : array();
+
+ if (array() === $urls) {
+ return array();
+ }
+
+ return ( new InformatieobjecttypeCatalog() )->group_by_supplier( $urls );
+ },
+ /**
+ * Suppliers for which filtering zaken on zaaktype is supported. Zaaktype filtering is
+ * disabled by default, so a supplier only has it applied once explicitly selected here.
+ *
+ * @return string[]
+ */
+ 'display.zaaktype-filtering-suppliers' => function ( Container $container ) {
+ $value = $container->make( 'zgw.settings', array( 'owc-mijn-services-zaaktype-filtering-suppliers' ) );
- return is_array( $value ) ? array_values( array_filter( $value ) ) : array();
+ return is_array( $value ) ? array_values( array_filter( $value, 'is_string' ) ) : array();
},
'display.disable-production-checks' => function ( Container $container ) {
return (bool) $container->make( 'zgw.settings', array( 'owc-mijn-services-disable-production-checks' ) );
@@ -100,9 +123,6 @@ function ( $client ) use ( $name ) {
'display.disable-kvk-filtering' => function ( Container $container ) {
return (bool) $container->make( 'zgw.settings', array( 'owc-mijn-services-disable-kvk-filtering' ) );
},
- 'display.enable-extended-kvk-filtering' => function ( Container $container ) {
- return (bool) $container->make( 'zgw.settings', array( 'owc-mijn-services-enable-extended-kvk-filtering' ) );
- },
'display.hide-status-steps-without-date' => function ( Container $container ) {
return (bool) $container->make( 'zgw.settings', array( 'owc-mijn-services-hide-status-steps-without-date' ) );
},
diff --git a/languages/owc-mijn-services.pot b/languages/owc-mijn-services.pot
index a42bdca..b342768 100644
--- a/languages/owc-mijn-services.pot
+++ b/languages/owc-mijn-services.pot
@@ -2,14 +2,14 @@
# This file is distributed under the EUPL.
msgid ""
msgstr ""
-"Project-Id-Version: OWC | Mijn Services 0.11.0\n"
+"Project-Id-Version: OWC | Mijn Services 0.17.0\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/owc-mijn-services\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"POT-Creation-Date: 2026-08-03T10:00:10+00:00\n"
+"POT-Creation-Date: 2026-09-22T12:17:51+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"X-Generator: WP-CLI 2.12.0\n"
"X-Domain: owc-mijn-services\n"
@@ -39,36 +39,38 @@ msgstr ""
msgid "https://www.yard.nl"
msgstr ""
-#: src/Blocks/Block.php:76
+#: src/Blocks/Block.php:93
msgid "Je moet ingelogd zijn om deze informatie te kunnen zien."
msgstr ""
-#: src/Blocks/Block.php:83
+#: src/Blocks/Block.php:100
msgid "Configureer minimaal één filteroptie: 'Filter op BSN' of 'Filter op KVK'."
msgstr ""
-#: src/Blocks/Block.php:91
+#: src/Blocks/Block.php:108
msgid "De filterinstellingen van dit blok zijn niet compatibel met uw inlogmethode. Neem contact op met de beheerder van deze website."
msgstr ""
-#: src/Blocks/Block.php:98
+#: src/Blocks/Block.php:117
msgid "Geen van de gekozen zaaksysteem leveranciers is geconfigureerd of ondersteunt zaken."
msgstr ""
-#: src/Blocks/Block.php:105
+#: src/Blocks/Block.php:125
#: src/Blocks/Zaak.php:48
msgid "De gekozen zaaksysteem leverancier client is niet geconfigureerd."
msgstr ""
-#: src/Blocks/Block.php:109
+#: src/Blocks/Block.php:129
msgid "De gekozen zaaksysteem leverancier ondersteunt geen zaken."
msgstr ""
#: src/Blocks/MijnZaken.php:24
+#: resources/blocks/mijn-zaken/edit.js:377
msgid "Het persoonlijke zakenoverzicht"
msgstr ""
#: src/Blocks/MijnZaken.php:25
+#: resources/blocks/mijn-zaken/edit.js:381
msgid "Toont een overzicht van de zaken voor de ingelogde gebruiker."
msgstr ""
@@ -78,10 +80,12 @@ msgid "Er zijn geen zaken gevonden."
msgstr ""
#: src/Blocks/Zaak.php:32
+#: resources/blocks/zaak/edit.js:123
msgid "Detailpagina van een zaak"
msgstr ""
#: src/Blocks/Zaak.php:33
+#: resources/blocks/zaak/edit.js:127
msgid "Toont de details van een specifieke zaak."
msgstr ""
@@ -97,15 +101,16 @@ msgstr ""
msgid "Beveiliging"
msgstr ""
-#: src/Controllers/InformatieobjecttypenCacheController.php:51
+#: src/Controllers/InformatieobjecttypenCacheController.php:52
msgid "Informatieobjecttypen opnieuw ophalen"
msgstr ""
-#: src/Controllers/InformatieobjecttypenCacheController.php:63
+#: src/Controllers/InformatieobjecttypenCacheController.php:64
+#: src/Controllers/ZaaktypenCacheController.php:64
msgid "Je hebt geen toestemming om deze actie uit te voeren."
msgstr ""
-#: src/Controllers/InformatieobjecttypenCacheController.php:87
+#: src/Controllers/InformatieobjecttypenCacheController.php:88
msgid "De cache van de informatieobjecttypen is geleegd. Ze worden opnieuw opgehaald bij het volgende gebruik of bij de volgende geplande taak."
msgstr ""
@@ -113,86 +118,100 @@ msgstr ""
msgid "Het informatieobject kon niet worden gedownload."
msgstr ""
+#: src/Controllers/ZaaktypenCacheController.php:49
+msgid "Zaaktypen opnieuw ophalen"
+msgstr ""
+
+#: src/Controllers/ZaaktypenCacheController.php:86
+msgid "De zaaktypen zijn opnieuw opgehaald."
+msgstr ""
+
#: src/Macros/ZgwApiMacros.php:221
msgid "Ophalen van het resultaat is mislukt"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:65
+#: src/Settings/OptionsPageRegistrar.php:53
msgid "OWC Mijn Services"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:80
+#: src/Settings/OptionsPageRegistrar.php:72
msgid "Logboekinstellingen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:81
+#: src/Settings/OptionsPageRegistrar.php:73
msgid "Schakel deze optie in om het loggen van systeemactiviteiten en foutmeldingen te activeren. Dit kan nuttig zijn voor het opsporen en oplossen van problemen binnen de plug-in."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:93
+#: src/Settings/OptionsPageRegistrar.php:85
msgid "DOC- en DOCX-documenten uitsluiten"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:94
+#: src/Settings/OptionsPageRegistrar.php:86
msgid "Schakel deze optie in om DOC- en DOCX-documenten niet op te halen bij het tonen van een zaak."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:106
+#: src/Settings/OptionsPageRegistrar.php:98
msgid "Toegestane informatieobjecttypen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:107
+#: src/Settings/OptionsPageRegistrar.php:99
msgid "Selecteer de informatieobjecttypen die mogen worden gebruikt voor het ophalen van informatieobjecten bij een zaak. Laat dit veld leeg om alle informatieobjecten te tonen."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:122
-msgid "Informatieobjecttype toevoegen"
+#: src/Settings/OptionsPageRegistrar.php:112
+msgid "Zoek en selecteer informatieobjecttypen…"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:132
-msgid "Productiecontroles uitschakelen"
+#: src/Settings/OptionsPageRegistrar.php:120
+msgid "Zaaktypefiltering ondersteunde leveranciers"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:133
-msgid "Schakel deze optie in om de verplichting van het gebruik van de blokattributen 'Filter op BSN' of 'Filter op KVK' uit te zetten. Standaard zijn productiecontroles ingeschakeld en is minimaal één van beide filterattributen vereist."
+#: src/Settings/OptionsPageRegistrar.php:121
+msgid "Selecteer de leveranciers waarvan de Zaken API filteren op zaaktype ondersteunt. Alleen voor geselecteerde leveranciers wordt gefilterd op de zaaktypen die in het 'Mijn zaken'-blok zijn geselecteerd; voor niet geselecteerde leveranciers worden altijd alle zaken opgehaald."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:145
-msgid "Filtering op KVK uitschakelen"
+#: src/Settings/OptionsPageRegistrar.php:138
+msgid "De zaaktypen die gebruikt worden voor de zaaktypefilter in het 'Mijn zaken'-blok worden dagelijks automatisch ververst. Gebruik onderstaande knop om ze direct opnieuw op te halen."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:146
-msgid "Schakel deze optie in als de leverancier het filteren op KVK (eHerkenning) niet ondersteunt. Gebruikers die via eHerkenning zijn ingelogd kunnen dan geen zaken ophalen."
+#: src/Settings/OptionsPageRegistrar.php:142
+#: resources/blocks/mijn-zaken/edit.js:196
+#: resources/blocks/mijn-zaken/edit.js:210
+msgid "Zaaktypen"
+msgstr ""
+
+#: src/Settings/OptionsPageRegistrar.php:153
+msgid "Productiecontroles uitschakelen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:158
-msgid "Uitgebreide KVK-filtering inschakelen"
+#: src/Settings/OptionsPageRegistrar.php:154
+msgid "Schakel deze optie in om de verplichting van het gebruik van de blokattributen 'Filter op BSN' of 'Filter op KVK' uit te zetten. Standaard zijn productiecontroles ingeschakeld en is minimaal één van beide filterattributen vereist."
+msgstr ""
+
+#: src/Settings/OptionsPageRegistrar.php:166
+msgid "Filtering op KVK uitschakelen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:159
-msgid "Schakel deze optie in om bij het filteren op KVK (eHerkenning) ook te filteren op RSIN of vestigingsnummer, indien beschikbaar. Niet elke leverancier ondersteunt deze filterparameters."
+#: src/Settings/OptionsPageRegistrar.php:167
+msgid "Schakel deze optie in als de leverancier het filteren op KVK (eHerkenning) niet ondersteunt. Gebruikers die via eHerkenning zijn ingelogd kunnen dan geen zaken ophalen."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:171
+#: src/Settings/OptionsPageRegistrar.php:179
msgid "Statusstappen zonder datum verbergen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:172
+#: src/Settings/OptionsPageRegistrar.php:180
msgid "Schakel deze optie in om statusstappen zonder gezette datum te verbergen in het statusoverzicht van een zaak. Dit verbergt ook nog niet bereikte (toekomstige) stappen. Standaard worden alle statusstappen getoond."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:184
+#: src/Settings/OptionsPageRegistrar.php:192
msgid "Volgnummers verbergen"
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:185
+#: src/Settings/OptionsPageRegistrar.php:193
msgid "Schakel deze optie in om de volgnummers van statusstappen te verbergen in het statusoverzicht van een zaak. Standaard worden volgnummers getoond."
msgstr ""
-#: src/Settings/OptionsPageRegistrar.php:206
-msgid "Selecteer een informatieobjecttype"
-msgstr ""
-
#: src/Views/editor/metaboxes/gatekeeper-access-metabox.blade.php:8
msgid "Vereiste authenticatie"
msgstr ""
@@ -241,12 +260,28 @@ msgstr ""
msgid "Er is een onbekende fout opgetreden. Probeer het later opnieuw."
msgstr ""
+#: src/Views/owc-overview-zaken.blade.php:16
+msgid "Lopende zaken"
+msgstr ""
+
+#: src/Views/owc-overview-zaken.blade.php:19
+msgid "U heeft op dit moment geen lopende zaken."
+msgstr ""
+
+#: src/Views/owc-overview-zaken.blade.php:22
+msgid "Afgeronde zaken"
+msgstr ""
+
+#: src/Views/owc-overview-zaken.blade.php:24
+msgid "U heeft nog geen afgeronde zaken."
+msgstr ""
+
#: src/Views/owc-single-zaak.blade.php:13
#: src/Views/partials/nlds/denhaag/card.blade.php:12
msgid "Zaakomschrijving ontbreekt"
msgstr ""
-#: src/Views/owc-single-zaak.blade.php:36
+#: src/Views/owc-single-zaak.blade.php:38
msgid "Zaak niet gevonden."
msgstr ""
@@ -266,67 +301,147 @@ msgstr ""
msgid "Zaak resultaat"
msgstr ""
-#: src/Views/partials/owc-single-zaak-status.blade.php:48
+#: src/Views/partials/owc-single-zaak-status.blade.php:65
msgid "Status"
msgstr ""
-#: src/Views/partials/owc-single-zaak-status.blade.php:51
+#: src/Views/partials/owc-single-zaak-status.blade.php:68
msgid "Er is (momenteel) geen status beschikbaar."
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:57
-#: resources/blocks/zaak/edit.js:44
-msgid "Instellingen"
+#: resources/blocks/mijn-zaken/edit.js:25
+msgid "Standaard volgorde"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:26
+msgid "Startdatum"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:27
+msgid "Einddatum"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:29
+msgid "Publicatiedatum"
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:63
-#: resources/blocks/zaak/edit.js:50
+#: resources/blocks/mijn-zaken/edit.js:33
+msgid "Archiefactiedatum"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:37
+msgid "Registratiedatum"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:41
+msgid "Zaaknummer"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:143
+#: resources/blocks/zaak/edit.js:48
msgid "Zaaksystemen"
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:64
-msgid "Selecteer de zaaksystemen waaruit de zaken opgehaald moeten worden."
+#: resources/blocks/mijn-zaken/edit.js:149
+msgid "Er zijn nog geen zaaksystemen geconfigureerd in de ZGW API-instellingen."
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:90
-#: resources/blocks/zaak/edit.js:76
-msgid "Geen leveranciers geconfigureerd."
+#: resources/blocks/mijn-zaken/edit.js:157
+msgid "Uit welke zaaksystemen worden zaken opgehaald?"
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:99
-#: resources/blocks/zaak/edit.js:85
-msgid "Schakel de filteroptie Filter op BSN in."
+#: resources/blocks/mijn-zaken/edit.js:184
+msgid "Selecteer minimaal één zaaksysteem. Zonder selectie worden er geen zaken getoond."
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:100
-#: resources/blocks/zaak/edit.js:86
-msgid "Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK."
+#: resources/blocks/mijn-zaken/edit.js:202
+msgid "Zaaktypefiltering is uitgeschakeld voor de geselecteerde zaaksystemen. Schakel dit in bij Instellingen → OWC Mijn Services → 'Zaaktypefiltering ondersteunde leveranciers' om hier zaaktypen te kunnen selecteren."
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: build/zaak/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:128
-#: resources/blocks/zaak/edit.js:114
-msgid "Filteren op KVK is uitgeschakeld in de instellingen van de plugin."
+#: resources/blocks/mijn-zaken/edit.js:211
+msgid "Selecteer per zaaksysteem de zaaktypen waarop gefilterd moet worden. Selecteer geen zaaktypen om alle zaaktypen te tonen."
msgstr ""
-#: build/mijn-zaken/index.js:1
-#: resources/blocks/mijn-zaken/edit.js:135
+#: resources/blocks/mijn-zaken/edit.js:252
+msgid "Zichtbare zaken"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:257
+msgid "Persoonlijke zaken (DigiD)"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:261
+msgid "Zaken gekoppeld aan het BSN van de ingelogde gebruiker."
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:273
+msgid "Zet \"Persoonlijke zaken\" aan, anders worden er geen zaken getoond."
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:277
+msgid "Zet minimaal één van beide opties aan, anders worden er geen zaken getoond."
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:285
+msgid "Zakelijke zaken (eHerkenning) zijn uitgeschakeld in de plugin-instellingen."
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:292
+msgid "Zakelijke zaken (eHerkenning)"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:296
+msgid "Zaken gekoppeld aan het KVK-nummer van de ingelogde gebruiker."
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:310
+msgid "Weergave"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:315
+msgid "Tabbladen tonen"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:325
msgid "Aantal zaken"
msgstr ""
-#: build/zaak/index.js:1
-#: resources/blocks/zaak/edit.js:51
+#: resources/blocks/mijn-zaken/edit.js:337
+msgid "Sorteren op"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:346
+msgid "Volgorde"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:350
+msgid "Nieuwste eerst"
+msgstr ""
+
+#: resources/blocks/mijn-zaken/edit.js:357
+msgid "Oudste eerst"
+msgstr ""
+
+#: resources/blocks/zaak/edit.js:42
+msgid "Instellingen"
+msgstr ""
+
+#: resources/blocks/zaak/edit.js:49
msgid "Selecteer de zaaksystemen waarvan zaken op deze pagina getoond kunnen worden."
msgstr ""
+
+#: resources/blocks/zaak/edit.js:74
+msgid "Geen leveranciers geconfigureerd."
+msgstr ""
+
+#: resources/blocks/zaak/edit.js:83
+msgid "Schakel de filteroptie Filter op BSN in."
+msgstr ""
+
+#: resources/blocks/zaak/edit.js:84
+msgid "Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK."
+msgstr ""
+
+#: resources/blocks/zaak/edit.js:112
+msgid "Filteren op KVK is uitgeschakeld in de instellingen van de plugin."
+msgstr ""
diff --git a/owc-mijn-services.php b/owc-mijn-services.php
index 900919a..f11f38a 100644
--- a/owc-mijn-services.php
+++ b/owc-mijn-services.php
@@ -12,7 +12,7 @@
* Plugin Name: OWC | Mijn Services
* Plugin URI: https://github.com/OpenWebconcept/plugin-owc-mijn-services
* Description: Toon mijn zaken
- * Version: 0.14.0
+ * Version: 0.17.0
* Author: Yard | Digital Agency
* Author URI: https://www.yard.nl
* License: EUPL
@@ -31,7 +31,7 @@
exit;
}
-const VERSION = '0.14.0';
+const VERSION = '0.18.0';
define( 'OWC_MY_SERVICES_FILE', __FILE__ );
define( 'OWC_MY_SERVICES_DIR_PATH', plugin_dir_path( OWC_MY_SERVICES_FILE ) );
define( 'OWC_MY_SERVICES_PLUGIN_URL', plugins_url( '/', OWC_MY_SERVICES_FILE ) );
diff --git a/package-lock.json b/package-lock.json
index c1d30c1..cccb2d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,6 +21,7 @@
},
"devDependencies": {
"@wordpress/scripts": "^31.8.0",
+ "copy-webpack-plugin": "^10.2.4",
"mini-css-extract-plugin": "^2.9.4",
"sass": "^1.93.2"
}
diff --git a/package.json b/package.json
index 8598d23..1d326ec 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
},
"devDependencies": {
"@wordpress/scripts": "^31.8.0",
+ "copy-webpack-plugin": "^10.2.4",
"mini-css-extract-plugin": "^2.9.4",
"sass": "^1.93.2"
}
diff --git a/resources/blocks/mijn-zaken/block.json b/resources/blocks/mijn-zaken/block.json
index 4c2a27f..4e5d5bc 100644
--- a/resources/blocks/mijn-zaken/block.json
+++ b/resources/blocks/mijn-zaken/block.json
@@ -8,8 +8,10 @@
"attributes": {
"zaakClient": { "type": "string", "default": "" },
"zaakClients": { "type": "array", "default": [] },
+ "zaaktypen": { "type": "object", "default": {} },
"byBSN": { "type": "boolean", "default": true },
"byKVK": { "type": "boolean", "default": false },
+ "showTabs": { "type": "boolean", "default": true },
"perPage": {
"type": "number",
"default": 10
diff --git a/resources/blocks/mijn-zaken/edit.js b/resources/blocks/mijn-zaken/edit.js
index 4919921..8eef374 100644
--- a/resources/blocks/mijn-zaken/edit.js
+++ b/resources/blocks/mijn-zaken/edit.js
@@ -5,43 +5,88 @@ import { useEffect } from '@wordpress/element';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody,
- Disabled,
BaseControl,
CheckboxControl,
Notice,
+ Placeholder,
RangeControl,
SelectControl,
+ ToggleControl,
+ __experimentalVStack as VStack,
} from '@wordpress/components';
-import ServerSideRender from '@wordpress/server-side-render';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies.
*/
-import metadata from './block.json';
import './editor.css';
-export default function Edit( { attributes, setAttributes } ) {
+const ORDER_BY_OPTIONS = [
+ { label: __( 'Standaard volgorde', 'owc-mijn-services' ), value: '' },
+ { label: __( 'Startdatum', 'owc-mijn-services' ), value: 'startdatum' },
+ { label: __( 'Einddatum', 'owc-mijn-services' ), value: 'einddatum' },
+ {
+ label: __( 'Publicatiedatum', 'owc-mijn-services' ),
+ value: 'publicatiedatum',
+ },
+ {
+ label: __( 'Archiefactiedatum', 'owc-mijn-services' ),
+ value: 'archiefactiedatum',
+ },
+ {
+ label: __( 'Registratiedatum', 'owc-mijn-services' ),
+ value: 'registratiedatum',
+ },
+ {
+ label: __( 'Zaaknummer', 'owc-mijn-services' ),
+ value: 'identificatie',
+ },
+];
+
+const MIN_PER_PAGE = 1;
+const MAX_PER_PAGE = 25;
+
+const Edit = ( { attributes, setAttributes } ) => {
const {
zaakClient,
zaakClients,
+ zaaktypen,
byBSN,
byKVK,
+ showTabs,
perPage,
orderBy,
orderByDirection,
} = attributes;
- const min = 1;
- const max = 25;
-
const suppliers = ( window?.owcMyServices?.zaakClientOptions ?? [] ).filter(
( opt ) => opt.value !== ''
);
- const productionChecksEnabled = window?.owcMyServices?.productionChecksEnabled ?? true;
- const bsnKvkMissing = productionChecksEnabled && ! byBSN && ! ( byKVK && ! disableKVKFiltering );
- const disableKVKFiltering = window?.owcMyServices?.disableKVKFiltering ?? false;
+ const productionChecksEnabled =
+ window?.owcMyServices?.productionChecksEnabled ?? true;
+ const disableKVKFiltering =
+ window?.owcMyServices?.disableKVKFiltering ?? false;
+ const bsnKvkMissing =
+ productionChecksEnabled &&
+ ! byBSN &&
+ ! ( byKVK && ! disableKVKFiltering );
+
+ const zaaktypeOptionsBySupplier = window?.owcMyServices?.zaaktypeOptions ?? {};
+ const suppliersWithZaaktypeOptions = zaakClients.filter(
+ ( supplier ) => ( zaaktypeOptionsBySupplier[ supplier ] ?? [] ).length > 0
+ );
+
+ const toggleZaaktype = ( supplier, url, checked ) => {
+ const current = zaaktypen[ supplier ] ?? [];
+ const next = checked
+ ? [ ...current, url ]
+ : current.filter( ( value ) => value !== url );
+
+ setAttributes( {
+ zaaktypen: { ...zaaktypen, [ supplier ]: next },
+ } );
+ };
// Migrate legacy single zaakClient string to zaakClients array.
useEffect( () => {
@@ -50,159 +95,297 @@ export default function Edit( { attributes, setAttributes } ) {
}
}, [] );
+ const toggleSupplier = ( supplier, checked ) =>
+ setAttributes( {
+ zaakClients: checked
+ ? [ ...zaakClients, supplier ]
+ : zaakClients.filter( ( value ) => value !== supplier ),
+ } );
+
+ // Replace zaaktype selections pointing at a superseded zaaktype version with its current URL.
+ useEffect( () => {
+ const migrationsBySupplier =
+ window?.owcMyServices?.zaaktypeUrlMigrations ?? {};
+
+ if ( Object.keys( migrationsBySupplier ).length === 0 ) {
+ return;
+ }
+
+ let changed = false;
+ const next = {};
+
+ Object.keys( zaaktypen ).forEach( ( supplier ) => {
+ const migrations = migrationsBySupplier[ supplier ] ?? {};
+ const urls = zaaktypen[ supplier ] ?? [];
+
+ const migratedUrls = urls.map( ( url ) => {
+ if ( migrations[ url ] ) {
+ changed = true;
+
+ return migrations[ url ];
+ }
+
+ return url;
+ } );
+
+ next[ supplier ] = [ ...new Set( migratedUrls ) ];
+ } );
+
+ if ( changed ) {
+ setAttributes( { zaaktypen: next } );
+ }
+ }, [] );
+
return (
<>
- { suppliers.length > 0 ? (
-
+ { suppliers.length === 0 ? (
+
+ { __(
+ 'Er zijn nog geen zaaksystemen geconfigureerd in de ZGW API-instellingen.',
+ 'owc-mijn-services'
+ ) }
+
+ ) : (
+ <>
+
+ { __(
+ 'Uit welke zaaksystemen worden zaken opgehaald?',
+ 'owc-mijn-services'
+ ) }
+
+
+ { suppliers.map( ( supplier ) => (
+
+ toggleSupplier(
+ supplier.value,
+ checked
+ )
+ }
+ />
+ ) ) }
+
+ { zaakClients.length === 0 && (
+
+ { __(
+ 'Selecteer minimaal één zaaksysteem. Zonder selectie worden er geen zaken getoond.',
+ 'owc-mijn-services'
+ ) }
+
+ ) }
+ >
+ ) }
+
+
+ { zaakClients.length > 0 && (
+
+
+ { suppliersWithZaaktypeOptions.length === 0 ? (
+
+ { __(
+ 'Zaaktypefiltering is uitgeschakeld voor de geselecteerde zaaksystemen. Schakel dit in bij Instellingen → OWC Mijn Services → \'Zaaktypefiltering ondersteunde leveranciers\' om hier zaaktypen te kunnen selecteren.',
+ 'owc-mijn-services'
+ ) }
+
+ ) : (
+
+ { suppliersWithZaaktypeOptions.map( ( supplier ) => {
+ const options =
+ zaaktypeOptionsBySupplier[ supplier ] ?? [];
+
+ return (
+
+ { options.map( ( option ) => (
+
+ toggleZaaktype(
+ supplier,
+ option.value,
+ checked
+ )
+ }
+ />
+ ) ) }
+
+ );
+ } ) }
+
+ ) }
+
+
+ ) }
+
+
+
- { suppliers.map( ( supplier ) => (
- {
- const next = checked
- ? [ ...zaakClients, supplier.value ]
- : zaakClients.filter(
- ( v ) =>
- v !== supplier.value
- );
- setAttributes( { zaakClients: next } );
- } }
- />
- ) ) }
-
- ) : (
-
- { __(
- 'Geen leveranciers geconfigureerd.',
+ help={ __(
+ 'Zaken gekoppeld aan het BSN van de ingelogde gebruiker.',
'owc-mijn-services'
) }
-
- ) }
- { bsnKvkMissing && (
-
- { disableKVKFiltering
- ? __( 'Schakel de filteroptie Filter op BSN in.', 'owc-mijn-services' )
- : __( 'Selecteer minimaal één filteroptie: Filter op BSN of Filter op KVK.', 'owc-mijn-services' )
+ checked={ byBSN }
+ onChange={ ( value ) =>
+ setAttributes( { byBSN: value } )
}
-
- ) }
-
- setAttributes( {
- byBSN,
- } )
- }
- />
- { ! disableKVKFiltering && (
-
- setAttributes( {
- byKVK,
- } )
+ />
+ { bsnKvkMissing && (
+
+ { disableKVKFiltering
+ ? __(
+ 'Zet "Persoonlijke zaken" aan, anders worden er geen zaken getoond.',
+ 'owc-mijn-services'
+ )
+ : __(
+ 'Zet minimaal één van beide opties aan, anders worden er geen zaken getoond.',
+ 'owc-mijn-services'
+ ) }
+
+ ) }
+ { disableKVKFiltering ? (
+
+ { __(
+ 'Zakelijke zaken (eHerkenning) zijn uitgeschakeld in de plugin-instellingen.',
+ 'owc-mijn-services'
+ ) }
+
+ ) : (
+
+ setAttributes( { byKVK: value } )
+ }
+ />
+ ) }
+
+
+
+
+
+
+ setAttributes( { showTabs: value } )
}
/>
- ) }
- { disableKVKFiltering && (
-
- { __(
- 'Filteren op KVK is uitgeschakeld in de instellingen van de plugin.',
+
- ) }
-
- setAttributes( { perPage: value } )
- }
- />
-
- setAttributes( {
- orderBy: neworderBy,
- } )
- }
- />
- { orderBy && '' !== orderBy && (
+ value={ perPage }
+ min={ MIN_PER_PAGE }
+ max={ MAX_PER_PAGE }
+ onChange={ ( value ) =>
+ setAttributes( { perPage: value } )
+ }
+ />
- setAttributes( {
- orderByDirection: neworderByDirection,
- } )
+ label={ __( 'Sorteren op', 'owc-mijn-services' ) }
+ value={ orderBy }
+ options={ ORDER_BY_OPTIONS }
+ onChange={ ( value ) =>
+ setAttributes( { orderBy: value } )
}
/>
- ) }
+ { orderBy && (
+
+ setAttributes( {
+ orderByDirection: value,
+ } )
+ }
+ />
+ ) }
+
>
);
-}
+};
+
+export default Edit;
diff --git a/resources/blocks/mijn-zaken/editor.css b/resources/blocks/mijn-zaken/editor.css
index 5142e9d..5828a62 100644
--- a/resources/blocks/mijn-zaken/editor.css
+++ b/resources/blocks/mijn-zaken/editor.css
@@ -1 +1,7 @@
/* Block styles */
+
+.owc-my-services-zaaktypen-supplier-panel.components-panel__body {
+ margin: 0 0 8px;
+ border: 1px solid #ddd;
+}
+
diff --git a/resources/blocks/mijn-zaken/view.js b/resources/blocks/mijn-zaken/view.js
index 3d07ebe..92b89be 100644
--- a/resources/blocks/mijn-zaken/view.js
+++ b/resources/blocks/mijn-zaken/view.js
@@ -2,28 +2,52 @@ import { createRoot } from '@wordpress/element';
import { Tabs } from './components/Tabs';
import { CaseCard } from './components/CaseCard';
+const CardGroup = ( { cards } ) => (
+
+ { cards.map( ( card, index ) => (
+
+ ) ) }
+
+);
+
+const EmptyMessage = ( { message } ) => (
+ { message }
+);
+
+const renderTabPanel = ( tab ) => {
+ if ( tab.cards?.length ) {
+ return ;
+ }
+
+ if ( tab.html ) {
+ return
;
+ }
+
+ return ;
+};
+
document.addEventListener( 'DOMContentLoaded', () => {
const mounts = document.querySelectorAll(
'.js-nlds-denhaag-tab-component'
);
mounts.forEach( ( el ) => {
- const tabsData = JSON.parse( el.dataset.tabs );
+ const tabsEnabled = JSON.parse( el.dataset.tabsEnabled ?? 'true' );
+ const root = createRoot( el );
+
+ if ( ! tabsEnabled ) {
+ const cards = JSON.parse( el.dataset.cards ?? '[]' );
+
+ root.render( );
- const tabData = tabsData.map( ( tab ) => ( {
+ return;
+ }
+
+ const tabData = JSON.parse( el.dataset.tabs ).map( ( tab ) => ( {
label: tab.label,
- panelContent: tab.cards ? (
-
- { tab.cards.map( ( card, index ) => (
-
- ) ) }
-
- ) : (
-
- ),
+ panelContent: renderTabPanel( tab ),
} ) );
- const root = createRoot( el );
root.render( );
} );
} );
diff --git a/resources/blocks/zaak/edit.js b/resources/blocks/zaak/edit.js
index 4a10bef..4364142 100644
--- a/resources/blocks/zaak/edit.js
+++ b/resources/blocks/zaak/edit.js
@@ -5,18 +5,16 @@ import { useEffect } from '@wordpress/element';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import {
PanelBody,
- Disabled,
BaseControl,
CheckboxControl,
Notice,
+ Placeholder,
} from '@wordpress/components';
-import ServerSideRender from '@wordpress/server-side-render';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies.
*/
-import metadata from './block.json';
import './editor.css';
export default function Edit( { attributes, setAttributes } ) {
@@ -27,8 +25,8 @@ export default function Edit( { attributes, setAttributes } ) {
);
const productionChecksEnabled = window?.owcMyServices?.productionChecksEnabled ?? true;
- const bsnKvkMissing = productionChecksEnabled && ! byBSN && ! ( byKVK && ! disableKVKFiltering );
const disableKVKFiltering = window?.owcMyServices?.disableKVKFiltering ?? false;
+ const bsnKvkMissing = productionChecksEnabled && ! byBSN && ! ( byKVK && ! disableKVKFiltering );
// Migrate legacy single zaakClient string to zaakClients array.
useEffect( () => {
@@ -120,12 +118,17 @@ export default function Edit( { attributes, setAttributes } ) {
>
);
diff --git a/resources/js/admin/informatieobjecttypen-select.js b/resources/js/admin/informatieobjecttypen-select.js
new file mode 100644
index 0000000..9ad5871
--- /dev/null
+++ b/resources/js/admin/informatieobjecttypen-select.js
@@ -0,0 +1,21 @@
+/**
+ * Turns the grouped 'Toegestane informatieobjecttypen' select into a searchable,
+ * multi-select Select2 field so a long, per-supplier list stays easy to use.
+ *
+ * @package OWC_Mijn_Services
+ */
+( function ( $ ) {
+ 'use strict';
+
+ $( function () {
+ $( '.owcms-informatieobjecttypen-select' ).each( function () {
+ const $select = $( this );
+
+ $select.select2( {
+ width: '100%',
+ allowClear: true,
+ placeholder: $select.data( 'placeholder' ) || '',
+ } );
+ } );
+ } );
+} )( jQuery );
diff --git a/resources/scss/admin/informatieobjecttypen-select.scss b/resources/scss/admin/informatieobjecttypen-select.scss
new file mode 100644
index 0000000..c2cd7a3
--- /dev/null
+++ b/resources/scss/admin/informatieobjecttypen-select.scss
@@ -0,0 +1,45 @@
+/**
+ * Fixes Select2's search input sitting lower than the selected choice pills.
+ *
+ * WordPress admin's own input styles override Select2's `margin-top`/`line-height`
+ * on the inline search field, breaking the vertical alignment Select2 relies on.
+ * Flex-aligning the row instead makes it robust against that override.
+ *
+ * Scoped via the original select's id (Select2 always inserts its widget as the next
+ * sibling of the source element) instead of a custom container class, since Select2's
+ * `containerCssClass` option requires the 'full' build and errors on the core build.
+ */
+#owc-mijn-services-allowed-informatieobjecttypen + .select2-container {
+ .select2-selection--multiple {
+ .select2-selection__rendered {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ padding: 5px;
+ gap: 4px;
+ }
+
+ .select2-selection__choice {
+ margin: 0;
+ float: none;
+ }
+ }
+
+ .select2-search--inline {
+ margin: 0;
+ float: none;
+
+ .select2-search__field {
+ height: auto;
+ min-height: 0;
+ padding: 0;
+ margin: 0;
+ box-shadow: none;
+ line-height: normal;
+ }
+ }
+}
+
+#owc-mijn-services-allowed-informatieobjecttypen + .select2-container--default .select2-selection--multiple .select2-selection__clear {
+ margin-top: 0;
+}
diff --git a/resources/vendor/select2/LICENSE.md b/resources/vendor/select2/LICENSE.md
new file mode 100644
index 0000000..8cb8a2b
--- /dev/null
+++ b/resources/vendor/select2/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/resources/vendor/select2/select2.min.css b/resources/vendor/select2/select2.min.css
new file mode 100644
index 0000000..7c18ad5
--- /dev/null
+++ b/resources/vendor/select2/select2.min.css
@@ -0,0 +1 @@
+.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;padding:1px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
diff --git a/resources/vendor/select2/select2.min.js b/resources/vendor/select2/select2.min.js
new file mode 100644
index 0000000..091d4b5
--- /dev/null
+++ b/resources/vendor/select2/select2.min.js
@@ -0,0 +1,2 @@
+/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
+!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(u){var e=function(){if(u&&u.fn&&u.fn.select2&&u.fn.select2.amd)var e=u.fn.select2.amd;var t,n,r,h,o,s,f,g,m,v,y,_,i,a,b;function w(e,t){return i.call(e,t)}function l(e,t){var n,r,i,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"]||{};if(e){for(s=(e=e.split("/")).length-1,y.nodeIdCompat&&b.test(e[s])&&(e[s]=e[s].replace(b,"")),"."===e[0].charAt(0)&&h&&(e=h.slice(0,h.length-1).concat(e)),u=0;u":">",'"':""","'":"'","/":"/"};return"string"!=typeof e?e:String(e).replace(/[&<>"'\/\\]/g,function(e){return t[e]})},i.appendMany=function(e,t){if("1.7"===o.fn.jquery.substr(0,3)){var n=o();o.map(t,function(e){n=n.add(e)}),t=n}e.append(t)},i.__cache={};var n=0;return i.GetUniqueElementId=function(e){var t=e.getAttribute("data-select2-id");return null==t&&(e.id?(t=e.id,e.setAttribute("data-select2-id",t)):(e.setAttribute("data-select2-id",++n),t=n.toString())),t},i.StoreData=function(e,t,n){var r=i.GetUniqueElementId(e);i.__cache[r]||(i.__cache[r]={}),i.__cache[r][t]=n},i.GetData=function(e,t){var n=i.GetUniqueElementId(e);return t?i.__cache[n]&&null!=i.__cache[n][t]?i.__cache[n][t]:o(e).data(t):i.__cache[n]},i.RemoveData=function(e){var t=i.GetUniqueElementId(e);null!=i.__cache[t]&&delete i.__cache[t],e.removeAttribute("data-select2-id")},i}),e.define("select2/results",["jquery","./utils"],function(h,f){function r(e,t,n){this.$element=e,this.data=n,this.options=t,r.__super__.constructor.call(this)}return f.Extend(r,f.Observable),r.prototype.render=function(){var e=h('');return this.options.get("multiple")&&e.attr("aria-multiselectable","true"),this.$results=e},r.prototype.clear=function(){this.$results.empty()},r.prototype.displayMessage=function(e){var t=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var n=h(' '),r=this.options.get("translations").get(e.message);n.append(t(r(e.args))),n[0].className+=" select2-results__message",this.$results.append(n)},r.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},r.prototype.append=function(e){this.hideLoading();var t=[];if(null!=e.results&&0!==e.results.length){e.results=this.sort(e.results);for(var n=0;n",{class:"select2-results__options select2-results__options--nested"});p.append(l),s.append(a),s.append(p)}else this.template(e,t);return f.StoreData(t,"data",e),t},r.prototype.bind=function(t,e){var l=this,n=t.id+"-results";this.$results.attr("id",n),t.on("results:all",function(e){l.clear(),l.append(e.data),t.isOpen()&&(l.setClasses(),l.highlightFirstItem())}),t.on("results:append",function(e){l.append(e.data),t.isOpen()&&l.setClasses()}),t.on("query",function(e){l.hideMessages(),l.showLoading(e)}),t.on("select",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("unselect",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("open",function(){l.$results.attr("aria-expanded","true"),l.$results.attr("aria-hidden","false"),l.setClasses(),l.ensureHighlightVisible()}),t.on("close",function(){l.$results.attr("aria-expanded","false"),l.$results.attr("aria-hidden","true"),l.$results.removeAttr("aria-activedescendant")}),t.on("results:toggle",function(){var e=l.getHighlightedResults();0!==e.length&&e.trigger("mouseup")}),t.on("results:select",function(){var e=l.getHighlightedResults();if(0!==e.length){var t=f.GetData(e[0],"data");"true"==e.attr("aria-selected")?l.trigger("close",{}):l.trigger("select",{data:t})}}),t.on("results:previous",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e);if(!(n<=0)){var r=n-1;0===e.length&&(r=0);var i=t.eq(r);i.trigger("mouseenter");var o=l.$results.offset().top,s=i.offset().top,a=l.$results.scrollTop()+(s-o);0===r?l.$results.scrollTop(0):s-o<0&&l.$results.scrollTop(a)}}),t.on("results:next",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e)+1;if(!(n>=t.length)){var r=t.eq(n);r.trigger("mouseenter");var i=l.$results.offset().top+l.$results.outerHeight(!1),o=r.offset().top+r.outerHeight(!1),s=l.$results.scrollTop()+o-i;0===n?l.$results.scrollTop(0):ithis.$results.outerHeight()||o<0)&&this.$results.scrollTop(i)}},r.prototype.template=function(e,t){var n=this.options.get("templateResult"),r=this.options.get("escapeMarkup"),i=n(e,t);null==i?t.style.display="none":"string"==typeof i?t.innerHTML=r(i):h(t).append(i)},r}),e.define("select2/keys",[],function(){return{BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46}}),e.define("select2/selection/base",["jquery","../utils","../keys"],function(n,r,i){function o(e,t){this.$element=e,this.options=t,o.__super__.constructor.call(this)}return r.Extend(o,r.Observable),o.prototype.render=function(){var e=n(' ');return this._tabindex=0,null!=r.GetData(this.$element[0],"old-tabindex")?this._tabindex=r.GetData(this.$element[0],"old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),e.attr("title",this.$element.attr("title")),e.attr("tabindex",this._tabindex),e.attr("aria-disabled","false"),this.$selection=e},o.prototype.bind=function(e,t){var n=this,r=e.id+"-results";this.container=e,this.$selection.on("focus",function(e){n.trigger("focus",e)}),this.$selection.on("blur",function(e){n._handleBlur(e)}),this.$selection.on("keydown",function(e){n.trigger("keypress",e),e.which===i.SPACE&&e.preventDefault()}),e.on("results:focus",function(e){n.$selection.attr("aria-activedescendant",e.data._resultId)}),e.on("selection:update",function(e){n.update(e.data)}),e.on("open",function(){n.$selection.attr("aria-expanded","true"),n.$selection.attr("aria-owns",r),n._attachCloseHandler(e)}),e.on("close",function(){n.$selection.attr("aria-expanded","false"),n.$selection.removeAttr("aria-activedescendant"),n.$selection.removeAttr("aria-owns"),n.$selection.trigger("focus"),n._detachCloseHandler(e)}),e.on("enable",function(){n.$selection.attr("tabindex",n._tabindex),n.$selection.attr("aria-disabled","false")}),e.on("disable",function(){n.$selection.attr("tabindex","-1"),n.$selection.attr("aria-disabled","true")})},o.prototype._handleBlur=function(e){var t=this;window.setTimeout(function(){document.activeElement==t.$selection[0]||n.contains(t.$selection[0],document.activeElement)||t.trigger("blur",e)},1)},o.prototype._attachCloseHandler=function(e){n(document.body).on("mousedown.select2."+e.id,function(e){var t=n(e.target).closest(".select2");n(".select2.select2-container--open").each(function(){this!=t[0]&&r.GetData(this,"element").select2("close")})})},o.prototype._detachCloseHandler=function(e){n(document.body).off("mousedown.select2."+e.id)},o.prototype.position=function(e,t){t.find(".selection").append(e)},o.prototype.destroy=function(){this._detachCloseHandler(this.container)},o.prototype.update=function(e){throw new Error("The `update` method must be defined in child classes.")},o.prototype.isEnabled=function(){return!this.isDisabled()},o.prototype.isDisabled=function(){return this.options.get("disabled")},o}),e.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(e,t,n,r){function i(){i.__super__.constructor.apply(this,arguments)}return n.Extend(i,t),i.prototype.render=function(){var e=i.__super__.render.call(this);return e.addClass("select2-selection--single"),e.html(' '),e},i.prototype.bind=function(t,e){var n=this;i.__super__.bind.apply(this,arguments);var r=t.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",r).attr("role","textbox").attr("aria-readonly","true"),this.$selection.attr("aria-labelledby",r),this.$selection.on("mousedown",function(e){1===e.which&&n.trigger("toggle",{originalEvent:e})}),this.$selection.on("focus",function(e){}),this.$selection.on("blur",function(e){}),t.on("focus",function(e){t.isOpen()||n.$selection.trigger("focus")})},i.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},i.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},i.prototype.selectionContainer=function(){return e(" ")},i.prototype.update=function(e){if(0!==e.length){var t=e[0],n=this.$selection.find(".select2-selection__rendered"),r=this.display(t,n);n.empty().append(r);var i=t.title||t.text;i?n.attr("title",i):n.removeAttr("title")}else this.clear()},i}),e.define("select2/selection/multiple",["jquery","./base","../utils"],function(i,e,l){function n(e,t){n.__super__.constructor.apply(this,arguments)}return l.Extend(n,e),n.prototype.render=function(){var e=n.__super__.render.call(this);return e.addClass("select2-selection--multiple"),e.html(''),e},n.prototype.bind=function(e,t){var r=this;n.__super__.bind.apply(this,arguments),this.$selection.on("click",function(e){r.trigger("toggle",{originalEvent:e})}),this.$selection.on("click",".select2-selection__choice__remove",function(e){if(!r.isDisabled()){var t=i(this).parent(),n=l.GetData(t[0],"data");r.trigger("unselect",{originalEvent:e,data:n})}})},n.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},n.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},n.prototype.selectionContainer=function(){return i('× ')},n.prototype.update=function(e){if(this.clear(),0!==e.length){for(var t=[],n=0;n×');a.StoreData(r[0],"data",t),this.$selection.find(".select2-selection__rendered").prepend(r)}},e}),e.define("select2/selection/search",["jquery","../utils","../keys"],function(r,a,l){function e(e,t,n){e.call(this,t,n)}return e.prototype.render=function(e){var t=r(' ');this.$searchContainer=t,this.$search=t.find("input");var n=e.call(this);return this._transferTabIndex(),n},e.prototype.bind=function(e,t,n){var r=this,i=t.id+"-results";e.call(this,t,n),t.on("open",function(){r.$search.attr("aria-controls",i),r.$search.trigger("focus")}),t.on("close",function(){r.$search.val(""),r.$search.removeAttr("aria-controls"),r.$search.removeAttr("aria-activedescendant"),r.$search.trigger("focus")}),t.on("enable",function(){r.$search.prop("disabled",!1),r._transferTabIndex()}),t.on("disable",function(){r.$search.prop("disabled",!0)}),t.on("focus",function(e){r.$search.trigger("focus")}),t.on("results:focus",function(e){e.data._resultId?r.$search.attr("aria-activedescendant",e.data._resultId):r.$search.removeAttr("aria-activedescendant")}),this.$selection.on("focusin",".select2-search--inline",function(e){r.trigger("focus",e)}),this.$selection.on("focusout",".select2-search--inline",function(e){r._handleBlur(e)}),this.$selection.on("keydown",".select2-search--inline",function(e){if(e.stopPropagation(),r.trigger("keypress",e),r._keyUpPrevented=e.isDefaultPrevented(),e.which===l.BACKSPACE&&""===r.$search.val()){var t=r.$searchContainer.prev(".select2-selection__choice");if(0this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumSelectionLength",[],function(){function e(e,t,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("select",function(){r._checkIfMaximumSelected()})},e.prototype.query=function(e,t,n){var r=this;this._checkIfMaximumSelected(function(){e.call(r,t,n)})},e.prototype._checkIfMaximumSelected=function(e,n){var r=this;this.current(function(e){var t=null!=e?e.length:0;0=r.maximumSelectionLength?r.trigger("results:message",{message:"maximumSelected",args:{maximum:r.maximumSelectionLength}}):n&&n()})},e}),e.define("select2/dropdown",["jquery","./utils"],function(t,e){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t(' ');return e.attr("dir",this.options.get("dir")),this.$dropdown=e},n.prototype.bind=function(){},n.prototype.position=function(e,t){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),e.define("select2/dropdown/search",["jquery","../utils"],function(o,e){function t(){}return t.prototype.render=function(e){var t=e.call(this),n=o(' ');return this.$searchContainer=n,this.$search=n.find("input"),t.prepend(n),t},t.prototype.bind=function(e,t,n){var r=this,i=t.id+"-results";e.call(this,t,n),this.$search.on("keydown",function(e){r.trigger("keypress",e),r._keyUpPrevented=e.isDefaultPrevented()}),this.$search.on("input",function(e){o(this).off("keyup")}),this.$search.on("keyup input",function(e){r.handleSearch(e)}),t.on("open",function(){r.$search.attr("tabindex",0),r.$search.attr("aria-controls",i),r.$search.trigger("focus"),window.setTimeout(function(){r.$search.trigger("focus")},0)}),t.on("close",function(){r.$search.attr("tabindex",-1),r.$search.removeAttr("aria-controls"),r.$search.removeAttr("aria-activedescendant"),r.$search.val(""),r.$search.trigger("blur")}),t.on("focus",function(){t.isOpen()||r.$search.trigger("focus")}),t.on("results:all",function(e){null!=e.query.term&&""!==e.query.term||(r.showSearch(e)?r.$searchContainer.removeClass("select2-search--hide"):r.$searchContainer.addClass("select2-search--hide"))}),t.on("results:focus",function(e){e.data._resultId?r.$search.attr("aria-activedescendant",e.data._resultId):r.$search.removeAttr("aria-activedescendant")})},t.prototype.handleSearch=function(e){if(!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},t.prototype.showSearch=function(e,t){return!0},t}),e.define("select2/dropdown/hidePlaceholder",[],function(){function e(e,t,n,r){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n,r)}return e.prototype.append=function(e,t){t.results=this.removePlaceholder(t.results),e.call(this,t)},e.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},e.prototype.removePlaceholder=function(e,t){for(var n=t.slice(0),r=t.length-1;0<=r;r--){var i=t[r];this.placeholder.id===i.id&&n.splice(r,1)}return n},e}),e.define("select2/dropdown/infiniteScroll",["jquery"],function(n){function e(e,t,n,r){this.lastParams={},e.call(this,t,n,r),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return e.prototype.append=function(e,t){this.$loadingMore.remove(),this.loading=!1,e.call(this,t),this.showLoadingMore(t)&&(this.$results.append(this.$loadingMore),this.loadMoreIfNeeded())},e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("query",function(e){r.lastParams=e,r.loading=!0}),t.on("query:append",function(e){r.lastParams=e,r.loading=!0}),this.$results.on("scroll",this.loadMoreIfNeeded.bind(this))},e.prototype.loadMoreIfNeeded=function(){var e=n.contains(document.documentElement,this.$loadingMore[0]);if(!this.loading&&e){var t=this.$results.offset().top+this.$results.outerHeight(!1);this.$loadingMore.offset().top+this.$loadingMore.outerHeight(!1)<=t+50&&this.loadMore()}},e.prototype.loadMore=function(){this.loading=!0;var e=n.extend({},{page:1},this.lastParams);e.page++,this.trigger("query:append",e)},e.prototype.showLoadingMore=function(e,t){return t.pagination&&t.pagination.more},e.prototype.createLoadingMore=function(){var e=n(' '),t=this.options.get("translations").get("loadingMore");return e.html(t(this.lastParams)),e},e}),e.define("select2/dropdown/attachBody",["jquery","../utils"],function(f,a){function e(e,t,n){this.$dropdownParent=f(n.get("dropdownParent")||document.body),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var r=this;e.call(this,t,n),t.on("open",function(){r._showDropdown(),r._attachPositioningHandler(t),r._bindContainerResultHandlers(t)}),t.on("close",function(){r._hideDropdown(),r._detachPositioningHandler(t)}),this.$dropdownContainer.on("mousedown",function(e){e.stopPropagation()})},e.prototype.destroy=function(e){e.call(this),this.$dropdownContainer.remove()},e.prototype.position=function(e,t,n){t.attr("class",n.attr("class")),t.removeClass("select2"),t.addClass("select2-container--open"),t.css({position:"absolute",top:-999999}),this.$container=n},e.prototype.render=function(e){var t=f(" "),n=e.call(this);return t.append(n),this.$dropdownContainer=t},e.prototype._hideDropdown=function(e){this.$dropdownContainer.detach()},e.prototype._bindContainerResultHandlers=function(e,t){if(!this._containerResultsHandlersBound){var n=this;t.on("results:all",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:append",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:message",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("select",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("unselect",function(){n._positionDropdown(),n._resizeDropdown()}),this._containerResultsHandlersBound=!0}},e.prototype._attachPositioningHandler=function(e,t){var n=this,r="scroll.select2."+t.id,i="resize.select2."+t.id,o="orientationchange.select2."+t.id,s=this.$container.parents().filter(a.hasScroll);s.each(function(){a.StoreData(this,"select2-scroll-position",{x:f(this).scrollLeft(),y:f(this).scrollTop()})}),s.on(r,function(e){var t=a.GetData(this,"select2-scroll-position");f(this).scrollTop(t.y)}),f(window).on(r+" "+i+" "+o,function(e){n._positionDropdown(),n._resizeDropdown()})},e.prototype._detachPositioningHandler=function(e,t){var n="scroll.select2."+t.id,r="resize.select2."+t.id,i="orientationchange.select2."+t.id;this.$container.parents().filter(a.hasScroll).off(n),f(window).off(n+" "+r+" "+i)},e.prototype._positionDropdown=function(){var e=f(window),t=this.$dropdown.hasClass("select2-dropdown--above"),n=this.$dropdown.hasClass("select2-dropdown--below"),r=null,i=this.$container.offset();i.bottom=i.top+this.$container.outerHeight(!1);var o={height:this.$container.outerHeight(!1)};o.top=i.top,o.bottom=i.top+o.height;var s=this.$dropdown.outerHeight(!1),a=e.scrollTop(),l=e.scrollTop()+e.height(),c=ai.bottom+s,d={left:i.left,top:o.bottom},p=this.$dropdownParent;"static"===p.css("position")&&(p=p.offsetParent());var h={top:0,left:0};(f.contains(document.body,p[0])||p[0].isConnected)&&(h=p.offset()),d.top-=h.top,d.left-=h.left,t||n||(r="below"),u||!c||t?!c&&u&&t&&(r="below"):r="above",("above"==r||t&&"below"!==r)&&(d.top=o.top-h.top-s),null!=r&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+r),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+r)),this.$dropdownContainer.css(d)},e.prototype._resizeDropdown=function(){var e={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(e.minWidth=e.width,e.position="relative",e.width="auto"),this.$dropdown.css(e)},e.prototype._showDropdown=function(e){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},e}),e.define("select2/dropdown/minimumResultsForSearch",[],function(){function e(e,t,n,r){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),e.call(this,t,n,r)}return e.prototype.showSearch=function(e,t){return!(function e(t){for(var n=0,r=0;r ');return e.attr("dir",this.options.get("dir")),this.$container=e,this.$container.addClass("select2-container--"+this.options.get("theme")),u.StoreData(e[0],"element",this.$element),e},d}),e.define("jquery-mousewheel",["jquery"],function(e){return e}),e.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults","./select2/utils"],function(i,e,o,t,s){if(null==i.fn.select2){var a=["open","close","destroy"];i.fn.select2=function(t){if("object"==typeof(t=t||{}))return this.each(function(){var e=i.extend(!0,{},t);new o(i(this),e)}),this;if("string"!=typeof t)throw new Error("Invalid arguments for Select2: "+t);var n,r=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=s.GetData(this,"select2");null==e&&window.console&&console.error&&console.error("The select2('"+t+"') method was called on an element that is not using Select2."),n=e[t].apply(e,r)}),-1
+ */
+ protected array $zaaktypen_by_supplier = array();
+
+ /**
+ * Supplier name resolved for the legacy single `zaakClient` attribute.
+ *
+ * @since NEXT
+ */
+ protected string $single_client_supplier_name = '';
+
public function __construct()
{
$this->zaken_filter = new ZakenFilter();
@@ -91,6 +108,8 @@ final public function render( array $attributes, string $block_content, WP_Block
return owc_mijn_services_render_view( 'owc-error', array( 'message' => __( 'De filterinstellingen van dit blok zijn niet compatibel met uw inlogmethode. Neem contact op met de beheerder van deze website.', 'owc-mijn-services' ) ) );
}
+ $this->zaaktypen_by_supplier = $this->normalize_zaaktypen_attribute( $attributes['zaaktypen'] ?? array() );
+
if (is_array( $attributes['zaakClients'] ?? null ) && 0 < count( $attributes['zaakClients'] )) {
$this->setup_clients( $attributes['zaakClients'] );
@@ -99,8 +118,9 @@ final public function render( array $attributes, string $block_content, WP_Block
}
} else {
try {
- $supplier = is_string( $attributes['zaakClient'] ?? null ) && '' !== $attributes['zaakClient'] ? $attributes['zaakClient'] : (string) get_query_var( BlockServiceProvider::QUERY_VAR_SUPPLIER );
- $this->client = apiClientManager()->getClient( $supplier );
+ $supplier = is_string( $attributes['zaakClient'] ?? null ) && '' !== $attributes['zaakClient'] ? $attributes['zaakClient'] : (string) get_query_var( BlockServiceProvider::QUERY_VAR_SUPPLIER );
+ $this->client = apiClientManager()->getClient( $supplier );
+ $this->single_client_supplier_name = $supplier;
} catch (NotFoundException $e) {
return owc_mijn_services_render_view( 'owc-error', array( 'message' => __( 'De gekozen zaaksysteem leverancier client is niet geconfigureerd.', 'owc-mijn-services' ) ) );
}
@@ -113,6 +133,28 @@ final public function render( array $attributes, string $block_content, WP_Block
return $this->render_block( $attributes, $block_content, $block );
}
+ /**
+ * Normalizes the `zaaktypen` block attribute (a supplier name => zaaktype URLs map)
+ * into a strictly typed array, discarding anything that doesn't match the expected shape.
+ *
+ * @since NEXT
+ *
+ * @return array
+ */
+ private function normalize_zaaktypen_attribute( $zaaktypen ): array
+ {
+ if ( ! is_array( $zaaktypen )) {
+ return array();
+ }
+
+ return array_map(
+ function ( $urls ) {
+ return is_array( $urls ) ? array_values( array_filter( $urls, 'is_string' ) ) : array();
+ },
+ $zaaktypen
+ );
+ }
+
/**
* Returns true when at least one supplier is configured on the block,
* checking the multi-supplier array first and falling back to the legacy string.
@@ -149,7 +191,7 @@ private function add_zaken_filter_args_by_auth_method( array $attributes ): void
$has_kvk_identification = '' !== $this->kvk || '' !== $this->vestigings_nummer || '' !== $this->rsin;
if ($has_kvk_identification && isset( $attributes['byKVK'] ) && true === $attributes['byKVK'] && ! ContainerResolver::make()->get( 'display.disable-kvk-filtering' )) {
- $authentication_filter_applied = $this->add_kvk_filter( $this->zaken_filter, $this->rsin, $this->vestigings_nummer, $this->kvk ) || $authentication_filter_applied;
+ $authentication_filter_applied = $this->add_kvk_filter( $this->zaken_filter, $this->rsin, $this->vestigings_nummer ) || $authentication_filter_applied;
}
if ( ! $authentication_filter_applied) {
@@ -187,7 +229,11 @@ final protected function get_zaken(): Collection
return Collection::collect( array() );
}
- return $this->client->zaken()->filter( $this->zaken_filter );
+ $zaaktype_urls = $this->supports_zaaktype_filtering( $this->single_client_supplier_name )
+ ? $this->resolve_zaaktype_urls( $this->single_client_supplier_name, $this->zaaktypen_by_supplier[ $this->single_client_supplier_name ] ?? array() )
+ : array();
+
+ return Collection::collect( $this->fetch_zaken( $this->client, $zaaktype_urls ) );
}
/**
@@ -202,9 +248,11 @@ final protected function get_zaken_from_clients(): Collection
foreach ($this->clients as $supplier_name => $client) {
try {
- $zaken = $client->zaken()->filter( clone $this->zaken_filter );
+ $zaaktype_urls = $this->supports_zaaktype_filtering( $supplier_name )
+ ? $this->resolve_zaaktype_urls( $supplier_name, $this->zaaktypen_by_supplier[ $supplier_name ] ?? array() )
+ : array();
- foreach ($zaken->all() as $zaak) {
+ foreach ($this->fetch_zaken( $client, $zaaktype_urls ) as $zaak) {
$zaak->setValue( 'supplier', $supplier_name );
$all_zaken[] = $zaak;
}
@@ -216,6 +264,89 @@ final protected function get_zaken_from_clients(): Collection
return Collection::collect( $all_zaken );
}
+ /**
+ * Fetches zaken for a single client. When zaaktype URLs are given, issues one filtered
+ * request per zaaktype and merges the results, since the ZGW Zaken API filters on a
+ * single zaaktype per request. Without zaaktype URLs, all zaaktypen are fetched.
+ *
+ * @since NEXT
+ *
+ * @param string[] $zaaktype_urls
+ */
+ private function fetch_zaken( Client $client, array $zaaktype_urls ): array
+ {
+ if (array() === $zaaktype_urls) {
+ return $this->filter_zaken_by_authenticated_initiator(
+ (array) $client->zaken()->expandOnly( self::EXPAND_WITH_ROLLEN )->filter( clone $this->zaken_filter )->all()
+ );
+ }
+
+ $zaken = array();
+
+ foreach ($zaaktype_urls as $zaaktype_url) {
+ $filter = ( clone $this->zaken_filter )->add( 'zaaktype', $zaaktype_url );
+ $zaken = array_merge( $zaken, (array) $client->zaken()->expandOnly( self::EXPAND_WITH_ROLLEN )->filter( $filter )->all() );
+ }
+
+ return $this->filter_zaken_by_authenticated_initiator( $zaken );
+ }
+
+ /**
+ * The betrokkene and 'rol__omschrijvingGeneriek' query filters each match against the zaak's
+ * rollen independently, so a zaak where the authenticated user holds a different role (e.g.
+ * 'adviseur') next to someone else's initiator role would still be returned. Drops those zaken,
+ * matching the check the single zaak view performs.
+ *
+ * @since NEXT
+ *
+ * @param Zaak[] $zaken
+ * @return Zaak[]
+ */
+ private function filter_zaken_by_authenticated_initiator( array $zaken ): array
+ {
+ return array_values(
+ array_filter(
+ $zaken,
+ fn ( Zaak $zaak ) => $this->zaak_has_authenticated_initiator( $zaak, $this->bsn, $this->vestigings_nummer, $this->rsin )
+ )
+ );
+ }
+
+ /**
+ * Resolves any zaaktype URL that has been superseded by a newer version to that version's
+ * URL, so a block's saved zaaktype selection keeps matching zaken even when it was chosen
+ * before the zaaktype was re-versioned in the ZTC. Falls back to the URL as-is when no
+ * migration is known (e.g. the block hasn't triggered a zaaktypen fetch yet).
+ *
+ * @since NEXT
+ *
+ * @param string[] $urls
+ * @return string[]
+ */
+ private function resolve_zaaktype_urls( string $supplier_name, array $urls ): array
+ {
+ if (array() === $urls) {
+ return $urls;
+ }
+
+ $migrations = ZaaktypeAdapter::url_migrations( $supplier_name );
+
+ if (array() === $migrations) {
+ return $urls;
+ }
+
+ return array_values(
+ array_unique(
+ array_map(
+ function ( $url ) use ( $migrations ) {
+ return $migrations[ $url ] ?? $url;
+ },
+ $urls
+ )
+ )
+ );
+ }
+
/**
* Resolves a list of supplier names into API clients, skipping any that are not
* configured or do not support zaken.
@@ -251,15 +382,17 @@ final protected function get_zaak_informatie_objecten( Zaak $zaak ): Collection
return $zaakinformatie_objecten;
}
- $exclude_doc_docx = (bool) ContainerResolver::make()->get( 'display.exclude-doc-docx' );
- $allowed_informatieobjecttypen = (array) ContainerResolver::make()->get( 'display.allowed-informatieobjecttypen' );
+ $exclude_doc_docx = (bool) ContainerResolver::make()->get( 'display.exclude-doc-docx' );
+ $allowed_per_supplier = (array) ContainerResolver::make()->get( 'display.allowed-informatieobjecttypen' );
+ $supplier_name = (string) $zaak->getValue( 'supplier', '' );
+ $allowed_for_supplier = (array) ( $allowed_per_supplier[ $supplier_name ] ?? array() );
- if ( ! $exclude_doc_docx && empty( $allowed_informatieobjecttypen )) {
+ if ( ! $exclude_doc_docx && array() === $allowed_for_supplier ) {
return $zaakinformatie_objecten;
}
return $zaakinformatie_objecten->filter(
- function ( Zaakinformatieobject $zaakinformatie_object ) use ( $exclude_doc_docx, $allowed_informatieobjecttypen ) {
+ function ( Zaakinformatieobject $zaakinformatie_object ) use ( $exclude_doc_docx, $allowed_for_supplier ) {
if ( ! $zaakinformatie_object->informatieobject instanceof Enkelvoudiginformatieobject) {
return false;
}
@@ -270,7 +403,7 @@ function ( Zaakinformatieobject $zaakinformatie_object ) use ( $exclude_doc_docx
return false;
}
- if ( 0 < count( $allowed_informatieobjecttypen ) && ! in_array( $informatieobject->informatieobjecttype, $allowed_informatieobjecttypen, true )) {
+ if ( 0 < count( $allowed_for_supplier ) && ! in_array( $informatieobject->informatieobjecttype, $allowed_for_supplier, true )) {
return false;
}
diff --git a/src/Blocks/MijnZaken.php b/src/Blocks/MijnZaken.php
index d70170f..70b10eb 100644
--- a/src/Blocks/MijnZaken.php
+++ b/src/Blocks/MijnZaken.php
@@ -29,6 +29,9 @@ protected function render_block( array $attributes, string $block_content, WP_Bl
$this->handle_filter_ordering( $attributes );
+ // Make sure initiators are only allowed to see their own cases, and not those of other initiators.
+ $this->zaken_filter->add( 'rol__omschrijvingGeneriek', 'initiator' );
+
try {
if ( 0 < count( $this->clients )) {
$zaken = $this->get_zaken_from_clients();
@@ -56,11 +59,20 @@ function ( $zaak ) use ( $attributes ) {
);
}
+ $order = $this->resolve_order( $attributes );
+
+ if (null !== $order) {
+ [ $field, $reverse ] = $order;
+ $zaken = $zaken->sortByAttribute( $field, $reverse );
+ }
+
$zaken = $zaken->take( (int) ( $attributes['perPage'] ?? 10 ) );
return owc_mijn_services_render_view(
'owc-overview-zaken',
array(
+ 'show_tabs' => (bool) ( $attributes['showTabs'] ?? true ),
+ 'all_zaken' => $this->filter_and_map_zaken( $zaken, fn () => true ),
'current_zaken' => $this->get_current_zaken( $zaken ),
'completed_zaken' => $this->get_completed_zaken( $zaken ),
)
@@ -72,17 +84,39 @@ function ( $zaak ) use ( $attributes ) {
*/
protected function handle_filter_ordering( array $attributes ): void
{
- if ( ! is_string( $attributes['orderBy'] ?? null ) || '' === $attributes['orderBy']) {
+ $order = $this->resolve_order( $attributes );
+
+ if (null === $order) {
return;
}
- if ( ! is_string( $attributes['orderByDirection'] ?? null )) {
- $attributes['orderByDirection'] = '-';
- } elseif ( $attributes['orderByDirection'] !== '-' ) {
- $attributes['orderByDirection'] = '';
+ [ $field, $reverse ] = $order;
+
+ $this->zaken_filter->orderBy( sprintf( '%s%s', $reverse ? '-' : '', $field ) );
+ }
+
+ /**
+ * Resolves the validated orderBy field and direction from the block attributes. Shared by
+ * handle_filter_ordering(), which applies it to the live API request, and render_block(),
+ * which re-applies it as a PHP-side sort after fetching: merging zaken from multiple
+ * zaaktypen and/or suppliers means each individual API request comes back correctly ordered,
+ * but the merged result as a whole is not, so it must be re-sorted before it's truncated to
+ * perPage.
+ *
+ * @since 0.17.0
+ *
+ * @return array{0: string, 1: bool}|null Tuple of [orderBy field, reverse], or null when no
+ * ordering is configured.
+ */
+ private function resolve_order( array $attributes ): ?array
+ {
+ if ( ! is_string( $attributes['orderBy'] ?? null ) || '' === $attributes['orderBy']) {
+ return null;
}
- $this->zaken_filter->orderBy( sprintf( '%s%s', $attributes['orderByDirection'], $attributes['orderBy'] ) );
+ $reverse = ! is_string( $attributes['orderByDirection'] ?? null ) || '-' === $attributes['orderByDirection'];
+
+ return array( $attributes['orderBy'], $reverse );
}
/**
diff --git a/src/Blocks/Zaak.php b/src/Blocks/Zaak.php
index 3558bb1..7127719 100644
--- a/src/Blocks/Zaak.php
+++ b/src/Blocks/Zaak.php
@@ -61,6 +61,18 @@ protected function render_block( array $attributes, string $block_content, WP_Bl
$zaak->setValue( 'supplier', $supplier );
}
+ /**
+ * TEST.
+ */
+ // $zaak->setValue(
+ // 'verlenging',
+ // array(
+ // 'reden' => 'test',
+ // 'duur' => 'P42D',
+ // )
+ // );
+ // $zaak->setValue( 'opschorting', array( 'reden' => 'test' ) );
+
return owc_mijn_services_render_view(
'owc-single-zaak',
array(
@@ -88,7 +100,7 @@ protected function retrieve_zaak( string $identification ): ?ZaakEntity
$has_kvk_identification = '' !== $this->kvk || '' !== $this->vestigings_nummer || '' !== $this->rsin;
if ($has_kvk_identification && ! ContainerResolver::make()->get( 'display.disable-kvk-filtering' )) {
- $authentication_filter_applied = $this->add_kvk_filter( $this->zaken_filter, $this->rsin, $this->vestigings_nummer, $this->kvk ) || $authentication_filter_applied;
+ $authentication_filter_applied = $this->add_kvk_filter( $this->zaken_filter, $this->rsin, $this->vestigings_nummer ) || $authentication_filter_applied;
}
if ( ! $authentication_filter_applied) {
@@ -96,10 +108,17 @@ protected function retrieve_zaak( string $identification ): ?ZaakEntity
}
if (0 === count( $this->clients )) {
- $zaak = $this->client->zaken()->filter( $this->zaken_filter )->first() ?: null;
+ $zaak = $this->client->zaken()->expandOnly( self::EXPAND_WITH_ROLLEN )->filter( $this->zaken_filter )->first() ?: null;
} else {
$zaak = $this->retrieve_zaak_by_multiple_clients( $identification );
}
+
+ // The betrokkene filters above independently match against the zaak's rollen, so they
+ // don't guarantee it's the same role that is both the betrokkene and the initiator.
+ // Verify that in code instead, now that the rollen are available on the zaak.
+ if ($zaak instanceof ZaakEntity && ! $this->zaak_has_authenticated_initiator( $zaak, $this->bsn, $this->vestigings_nummer, $this->rsin )) {
+ $zaak = null;
+ }
} catch (Exception $e) {
LoggerService::log_exception( $e, array( 'context' => "Error retrieving zaak with identification '{$identification}'." ) );
@@ -118,7 +137,7 @@ protected function retrieve_zaak_by_multiple_clients( string $identification ):
foreach ($this->clients as $supplier_name => $client) {
try {
- $zaak = $client->zaken()->filter( clone $this->zaken_filter )->first() ?: null;
+ $zaak = $client->zaken()->expandOnly( self::EXPAND_WITH_ROLLEN )->filter( clone $this->zaken_filter )->first() ?: null;
if (null !== $zaak) {
$zaak->setValue( 'supplier', $supplier_name );
diff --git a/src/CMB2/SelectOptgroupType.php b/src/CMB2/SelectOptgroupType.php
index e573590..f537567 100644
--- a/src/CMB2/SelectOptgroupType.php
+++ b/src/CMB2/SelectOptgroupType.php
@@ -3,7 +3,7 @@
declare(strict_types=1);
/**
- * CMB2 select field type that renders grouped options as elements.
+ * CMB2 multi-select field type that renders grouped options as elements.
*
* @package OWC_Mijn_Services
* @author Yard | Digital Agency
@@ -22,11 +22,12 @@
use CMB2_Type_Select;
/**
- * CMB2 select field type that renders grouped options as elements.
+ * CMB2 multi-select field type that renders grouped options as elements.
*
* Field options are expected as a `value => label` map, where any entry whose value is itself
* an array is rendered as an (keyed by group label) containing its own `value => label` options.
- * Flat and grouped entries may be mixed.
+ * Flat and grouped entries may be mixed. Renders as a `` so several options,
+ * across groups, can be selected at once.
*
* @since 0.12.0
*/
@@ -37,11 +38,12 @@ public function render()
$attrs = $this->parse_args(
'select',
array(
- 'class' => 'cmb2_select',
- 'name' => $this->_name(),
- 'id' => $this->_id(),
- 'desc' => $this->_desc( true ),
- 'options' => $this->concat_grouped_items(),
+ 'class' => 'cmb2_select',
+ 'name' => $this->_name() . '[]',
+ 'id' => $this->_id(),
+ 'desc' => $this->_desc( true ),
+ 'multiple' => 'multiple',
+ 'options' => $this->concat_grouped_items(),
)
);
@@ -56,30 +58,22 @@ private function concat_grouped_items(): string
{
$field = $this->field;
- $value = trim( (string) ( null !== $field->escaped_value() ? $field->escaped_value() : $field->get_default() ) );
+ $value = (array) ( null !== $field->escaped_value() ? $field->escaped_value() : $field->get_default() );
- $options = array();
-
- if ($option_none = $field->args( 'show_option_none' )) {
- $options[''] = $option_none;
- }
-
- $options = $options + (array) $field->options();
-
- return $this->handle_options( $options, $value );
+ return $this->handle_options( (array) $field->options(), $value );
}
/**
* Handle options and return the HTML string for the and elements.
*/
- private function handle_options( array $options, string $value ): string
+ private function handle_options( array $options, array $selected_values ): string
{
$html = '';
$iterator = 1;
foreach ($options as $key => $option) {
if ( ! is_array( $option )) {
- $html .= $this->render_option( $key, $option, $value, $iterator++ );
+ $html .= $this->render_option( $key, $option, $selected_values, $iterator++ );
continue;
}
@@ -87,7 +81,7 @@ private function handle_options( array $options, string $value ): string
$html .= sprintf( '', esc_attr( $key ) );
foreach ($option as $opt_value => $opt_label) {
- $html .= $this->render_option( $opt_value, $opt_label, $value, $iterator++ );
+ $html .= $this->render_option( $opt_value, $opt_label, $selected_values, $iterator++ );
}
$html .= ' ';
@@ -96,14 +90,14 @@ private function handle_options( array $options, string $value ): string
return $html;
}
- private function render_option( string $opt_value, string $opt_label, string $selected_value, int $iterator ): string
+ private function render_option( string $opt_value, string $opt_label, array $selected_values, int $iterator ): string
{
$args = array(
'value' => $opt_value,
'label' => $opt_label,
);
- if ($selected_value === $opt_value) {
+ if (in_array( $opt_value, $selected_values, true )) {
$args['checked'] = 'checked';
}
diff --git a/src/Controllers/InformatieobjecttypenCacheController.php b/src/Controllers/InformatieobjecttypenCacheController.php
index 6954e8b..8cf6c18 100644
--- a/src/Controllers/InformatieobjecttypenCacheController.php
+++ b/src/Controllers/InformatieobjecttypenCacheController.php
@@ -20,6 +20,7 @@
}
use OWC\My_Services\Settings\Adapters\InformatieobjecttypeAdapter;
+use OWC\My_Services\Settings\OptionsPageRegistrar;
use OWC\My_Services\Traits\Supplier;
/**
@@ -57,7 +58,7 @@ public function render_clear_cache_button(): void
*/
public function handle_clear_cache_request(): void
{
- $capability = apply_filters( 'owcms::settings/allowed_informatieobjecttypen_capability', 'manage_options' );
+ $capability = OptionsPageRegistrar::get_allowed_settings_capability();
if ( ! current_user_can( 'manage_options' ) && ! current_user_can( $capability )) {
wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', 'owc-mijn-services' ) );
diff --git a/src/Controllers/ZaaktypenCacheController.php b/src/Controllers/ZaaktypenCacheController.php
new file mode 100644
index 0000000..26e9e38
--- /dev/null
+++ b/src/Controllers/ZaaktypenCacheController.php
@@ -0,0 +1,89 @@
+%s
',
+ esc_url( $url ),
+ esc_html__( 'Zaaktypen opnieuw ophalen', 'owc-mijn-services' )
+ );
+ }
+
+ /**
+ * Handles the request to manually re-fetch the zaaktypen of all configured suppliers.
+ *
+ * Fetches live rather than merely clearing the cache, since this is an explicit,
+ * user-initiated action where waiting for the request to finish is expected.
+ */
+ public function handle_refetch_request(): void
+ {
+ $capability = OptionsPageRegistrar::get_allowed_settings_capability();
+
+ if ( ! current_user_can( 'manage_options' ) && ! current_user_can( $capability )) {
+ wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', 'owc-mijn-services' ) );
+ }
+
+ check_admin_referer( self::ACTION, self::NONCE_QUERY );
+
+ ( new PopulateZaaktypen() )->init();
+
+ wp_safe_redirect( add_query_arg( self::NOTICE_FLAG, '1', wp_get_referer() ?: admin_url() ) );
+ exit;
+ }
+
+ /**
+ * Shows an admin notice right after the zaaktypen have been re-fetched.
+ */
+ public function render_refetched_notice(): void
+ {
+ if ( ! isset( $_GET[ self::NOTICE_FLAG ] )) {
+ return;
+ }
+
+ printf(
+ '',
+ esc_html__( 'De zaaktypen zijn opnieuw opgehaald.', 'owc-mijn-services' )
+ );
+ }
+}
diff --git a/src/Macros/ZgwApiMacros.php b/src/Macros/ZgwApiMacros.php
index de04145..33803b0 100644
--- a/src/Macros/ZgwApiMacros.php
+++ b/src/Macros/ZgwApiMacros.php
@@ -21,6 +21,7 @@
use Exception;
use DateTimeImmutable;
+use DateInterval;
use OWC\My_Services\Services\LoggerService;
use OWC\ZGW\Entities\Enkelvoudiginformatieobject;
use OWC\ZGW\Entities\Statustype;
@@ -41,7 +42,7 @@ public static function register(): void
{
Zaak::macro(
'permalink',
- function () {
+ function (): string {
$identification = $this->getValue( 'identificatie', '' );
if ('' === $identification) {
@@ -60,7 +61,7 @@ function () {
Zaak::macro(
'startDate',
- function ( string $format = 'j F Y' ) {
+ function ( string $format = 'j F Y' ): string {
$startDate = $this->getValue( 'startdatum', null );
if ( ! $startDate instanceof DateTimeImmutable) {
@@ -73,7 +74,7 @@ function ( string $format = 'j F Y' ) {
Zaak::macro(
'registerDate',
- function ( string $format = 'j F Y' ) {
+ function ( string $format = 'j F Y' ): string {
$register_date = $this->getValue( 'registratiedatum', null );
if ( ! $register_date instanceof DateTimeImmutable) {
@@ -99,7 +100,7 @@ function ( string $format = 'j F Y' ): ?string {
Zaak::macro(
'endDatePlanned',
- function ( string $format = 'j F Y' ) {
+ function ( string $format = 'j F Y' ): string {
$end_date_planned = $this->getValue( 'einddatumGepland', null );
if ( ! $end_date_planned instanceof DateTimeImmutable) {
@@ -112,9 +113,9 @@ function ( string $format = 'j F Y' ) {
Enkelvoudiginformatieobject::macro(
'downloadUrl',
- function ( string $zaak_identification, string $supplier ) {
+ function ( string $zaak_identification, string $supplier ): string {
if ($this->isClassified() || ! $this->hasFinalStatus()) {
- // return ''; // Is disabled for testing purposes.
+ return '';
}
$identification = $this->identification();
@@ -128,7 +129,7 @@ function ( string $zaak_identification, string $supplier ) {
Enkelvoudiginformatieobject::macro(
'identification',
- function () {
+ function (): string {
$url = $this->getValue( 'url', '' );
if ( ! is_string( $url ) || '' === $url) {
@@ -143,7 +144,7 @@ function () {
Enkelvoudiginformatieobject::macro(
'sizeFormatted',
- function () {
+ function (): string {
$size = $this->getValue( 'bestandsomvang', 0 );
return $size ? ( size_format( $size ) ?: '' ) : '';
@@ -152,7 +153,7 @@ function () {
Enkelvoudiginformatieobject::macro(
'formatType',
- function () {
+ function (): string {
$mime_type = $this->getValue( 'formaat', '' );
if ( ! is_string( $mime_type ) || 1 > strlen( $mime_type )) {
@@ -167,7 +168,7 @@ function () {
Enkelvoudiginformatieobject::macro(
'creationDate',
- function () {
+ function (): string {
$date = $this->getValue( 'creatiedatum', null );
if ( ! $date instanceof DateTimeImmutable) {
@@ -184,7 +185,7 @@ function () {
Enkelvoudiginformatieobject::macro(
'formattedMetaData',
- function () {
+ function (): string {
$meta = array_filter(
array(
$this->formatType(),
@@ -203,7 +204,7 @@ function () {
Statustype::macro(
'volgnummer',
- function () {
+ function (): string {
$volgnummer = (string) $this->getValue( 'volgnummer', '' );
return ltrim( $volgnummer, '0' );
@@ -212,9 +213,9 @@ function () {
Zaak::macro(
'result',
- function () {
+ function (): string {
try {
- return $this->getValue( 'resultaat' )?->resultaattype?->getValue( 'toelichting' );
+ return $this->getValue( 'resultaat' )?->resultaattype?->getValue( 'toelichting', '' ) ?? '';
} catch (Exception $e) {
LoggerService::log_exception( $e, array( 'context' => "Unable to get 'Zaak resultaat'" ) );
@@ -222,5 +223,117 @@ function () {
}
}
);
+
+ /**
+ * @since NEXT
+ */
+ Zaak::macro(
+ 'extensionReason',
+ function (): string {
+ try {
+ return $this->getValue( 'verlenging' )['reden'] ?? '';
+ } catch (Exception $e) {
+ LoggerService::log_exception( $e, array( 'context' => "Unable to get 'Zaak verlenging reden'" ) );
+
+ return '';
+ }
+ }
+ );
+
+ /**
+ * @since NEXT
+ */
+ Zaak::macro(
+ 'extensionDuration',
+ function (): string {
+ try {
+ return $this->getValue( 'verlenging' )['duur'] ?? '';
+ } catch (Exception $e) {
+ LoggerService::log_exception( $e, array( 'context' => "Unable to get 'Zaak verlenging duur'" ) );
+
+ return '';
+ }
+ }
+ );
+
+ /**
+ * @since NEXT
+ */
+ Zaak::macro(
+ 'extensionDurationFormatted',
+ function (): string {
+ try {
+ $period = $this->extensionDuration();
+
+ if ('' === $period) {
+ return '';
+ }
+
+ $interval = new DateInterval( $period );
+
+ $units = array(
+ 'y' => array( 'jaar', 'jaren' ),
+ 'm' => array( 'maand', 'maanden' ),
+ 'd' => array( 'dag', 'dagen' ),
+ 'h' => array( 'uur', 'uren' ),
+ 'i' => array( 'minuut', 'minuten' ),
+ );
+
+ $parts = array();
+
+ foreach ( $units as $property => list( $singular, $plural ) ) {
+ $value = $interval->{$property};
+
+ if ( $value > 0 ) {
+ $parts[] = sprintf(
+ '%d %s',
+ $value,
+ 1 === $value ? $singular : $plural
+ );
+ }
+ }
+
+ return implode( ', ', $parts );
+ } catch (Exception $e) {
+ LoggerService::log_exception( $e, array( 'context' => "Unable to get 'Zaak verlenging duur'" ) );
+
+ return '';
+ }
+ }
+ );
+
+ /**
+ * @since NEXT
+ */
+ Zaak::macro(
+ 'extensionFormatted',
+ function (): string {
+ $reason = $this->extensionReason();
+
+ if ('' === $reason) {
+ return '';
+ }
+
+ $duration = $this->extensionDurationFormatted();
+
+ return '' === $duration ? $reason : sprintf( '%s (%s)', $reason, $duration );
+ }
+ );
+
+ /**
+ * @since NEXT
+ */
+ Zaak::macro(
+ 'suspensionReason',
+ function (): string {
+ try {
+ return $this->getValue( 'opschorting' )['reden'] ?? '';
+ } catch (Exception $e) {
+ LoggerService::log_exception( $e, array( 'context' => "Unable to get 'Zaak opschorting reden'" ) );
+
+ return '';
+ }
+ }
+ );
}
}
diff --git a/src/Providers/BlockServiceProvider.php b/src/Providers/BlockServiceProvider.php
index 3401315..5f43703 100644
--- a/src/Providers/BlockServiceProvider.php
+++ b/src/Providers/BlockServiceProvider.php
@@ -19,13 +19,17 @@
exit;
}
+use DI\NotFoundException;
use OWC\My_Services\Blocks\MijnZaken;
use OWC\My_Services\Blocks\Zaak;
use OWC\My_Services\ContainerResolver;
+use OWC\My_Services\Settings\Adapters\ZaaktypeAdapter;
use WP_Block_Editor_Context;
use WP_Block_Type;
use OWC\My_Services\Traits\Supplier;
+use function OWC\ZGW\apiClientManager;
+
/**
* Register block service provider.
*
@@ -130,6 +134,10 @@ private function register_mijn_zaken_block(): void
'type' => 'array',
'default' => array(),
),
+ 'zaaktypen' => array(
+ 'type' => 'object',
+ 'default' => array(),
+ ),
'byBSN' => array(
'type' => 'boolean',
'default' => true,
@@ -138,6 +146,10 @@ private function register_mijn_zaken_block(): void
'type' => 'boolean',
'default' => false,
),
+ 'showTabs' => array(
+ 'type' => 'boolean',
+ 'default' => true,
+ ),
'perPage' => array(
'type' => 'number',
'default' => 10,
@@ -159,6 +171,7 @@ private function register_mijn_zaken_block(): void
}
$this->preprare_and_add_client_options( $block_type );
+ $this->add_zaaktype_options( $block_type );
}
/**
@@ -208,29 +221,16 @@ private function preprare_and_add_client_options( WP_Block_Type $block_type ): v
return;
}
- $clients = $this->get_configured_suppliers();
-
- $options = array(
- array(
- 'value' => '',
- 'label' => 'Selecteer een leverancier',
- ),
+ $options = array_map(
+ function ( $client ) {
+ return array(
+ 'value' => $client['name'],
+ 'label' => $client['name'],
+ );
+ },
+ $this->get_configured_suppliers()
);
- if (0 < count( $clients )) {
- $prepared_client_options = array_map(
- function ( $client ) {
- return array(
- 'value' => $client['name'],
- 'label' => $client['name'],
- );
- },
- $clients
- );
-
- $options = array_merge( $options, $prepared_client_options );
- }
-
$production_checks_enabled = ! ContainerResolver::make()->get( 'display.disable-production-checks' );
$disable_kvk_filtering = ContainerResolver::make()->get( 'display.disable-kvk-filtering' );
@@ -247,6 +247,81 @@ function ( $client ) {
$this->zaak_client_options_inline_script_added = true;
}
+ /**
+ * Localizes the zaaktypen of every configured supplier that supports them, grouped
+ * per supplier, so the block editor can offer a per-supplier zaaktype filter.
+ *
+ * Skips suppliers not selected in the 'Zaaktypefiltering ondersteunde leveranciers' setting,
+ * and localizes nothing at all when no supplier is selected there, so the block editor never
+ * receives zaaktype options for a supplier it can't actually filter on.
+ *
+ * Only runs in wp-admin: the result is only ever read by the block editor, and the
+ * underlying zaaktypen are already cached in a transient (kept warm by a daily cron),
+ * so this stays cheap even though it runs on every editor screen load.
+ *
+ * @since 0.17.0
+ */
+ private function add_zaaktype_options( WP_Block_Type $block_type ): void
+ {
+ if ( ! is_admin() ) {
+ return;
+ }
+
+ $filtering_suppliers = ContainerResolver::make()->get( 'display.zaaktype-filtering-suppliers' );
+
+ if (array() === $filtering_suppliers) {
+ return;
+ }
+
+ $grouped = array();
+ $migrations = array();
+
+ foreach ($this->get_configured_suppliers() as $supplier) {
+ if ( ! in_array( $supplier['name'], $filtering_suppliers, true )) {
+ continue;
+ }
+
+ try {
+ $client = apiClientManager()->getClient( $supplier['name'] );
+ } catch (NotFoundException $e) {
+ continue;
+ }
+
+ if ( ! $client->supports( 'zaaktypen' )) {
+ continue;
+ }
+
+ $types = ( new ZaaktypeAdapter( $client, $supplier['name'] ) )->handle();
+
+ if (array() === $types) {
+ continue;
+ }
+
+ $grouped[ $supplier['name'] ] = array_map(
+ function ( $url, $label ) {
+ return array(
+ 'value' => $url,
+ 'label' => $label,
+ );
+ },
+ array_keys( $types ),
+ $types
+ );
+
+ $supplier_migrations = ZaaktypeAdapter::url_migrations( $supplier['name'] );
+
+ if (array() !== $supplier_migrations) {
+ $migrations[ $supplier['name'] ] = $supplier_migrations;
+ }
+ }
+
+ wp_add_inline_script(
+ $block_type->editor_script,
+ 'window.owcMyServices = window.owcMyServices || {}; window.owcMyServices.zaaktypeOptions = ' . wp_json_encode( $grouped ) . '; window.owcMyServices.zaaktypeUrlMigrations = ' . wp_json_encode( $migrations ) . ';',
+ 'before'
+ );
+ }
+
public function maybe_handle_information_object_download( string $template ): string
{
if (get_query_var( 'pagename' ) === 'zaak-download') {
diff --git a/src/Providers/SettingsServiceProvider.php b/src/Providers/SettingsServiceProvider.php
index 2e06a6d..6cd31f6 100644
--- a/src/Providers/SettingsServiceProvider.php
+++ b/src/Providers/SettingsServiceProvider.php
@@ -20,6 +20,7 @@
}
use OWC\My_Services\Controllers\InformatieobjecttypenCacheController;
+use OWC\My_Services\Controllers\ZaaktypenCacheController;
use OWC\My_Services\Settings\OptionsPageRegistrar;
use OWC\ZGW\ApiClientManager;
use OWC\ZGW\WordPress\ClientProvider;
@@ -43,8 +44,16 @@ public function register(): void
add_action( 'cmb2_admin_init', ( new OptionsPageRegistrar() )->addSettingsFields( ... ) );
+ // Informatieobjecttypen cache actions.
add_action( 'admin_post_' . InformatieobjecttypenCacheController::ACTION, ( new InformatieobjecttypenCacheController() )->handle_clear_cache_request( ... ) );
add_action( 'admin_notices', ( new InformatieobjecttypenCacheController() )->render_cache_cleared_notice( ... ) );
+
+ // Zaaktypen cache actions.
+ add_action( 'admin_post_' . ZaaktypenCacheController::ACTION, ( new ZaaktypenCacheController() )->handle_refetch_request( ... ) );
+ add_action( 'admin_notices', ( new ZaaktypenCacheController() )->render_refetched_notice( ... ) );
+
+ // Enqueue scripts for the settings page.
+ add_action( 'admin_enqueue_scripts', $this->enqueue_scripts( ... ) );
}
/**
@@ -53,4 +62,35 @@ public function register(): void
public function boot(): void
{
}
+
+ /**
+ * Loads Select2 for the grouped 'Toegestane informatieobjecttypen' select, only on this
+ * plugin's own settings page.
+ *
+ * @since 0.17.0
+ */
+ public function enqueue_scripts( string $hook_suffix ): void
+ {
+ if ('settings_page_owc_mijn_services_settings' !== $hook_suffix) {
+ return;
+ }
+
+ wp_enqueue_style( 'owc-mijn-services-select2', owc_mijn_services_asset_url( 'select2/select2.min.css' ), array(), \VERSION );
+ wp_enqueue_script( 'owc-mijn-services-select2', owc_mijn_services_asset_url( 'select2/select2.min.js' ), array( 'jquery' ), \VERSION, true );
+
+ wp_enqueue_style(
+ 'owc-mijn-services-informatieobjecttypen-select-style',
+ owc_mijn_services_asset_url( 'css/informatieobjecttypen-select.css' ),
+ array( 'owc-mijn-services-select2' ),
+ \VERSION
+ );
+
+ wp_enqueue_script(
+ 'owc-mijn-services-informatieobjecttypen-select',
+ owc_mijn_services_url( 'resources/js/admin/informatieobjecttypen-select.js' ),
+ array( 'jquery', 'owc-mijn-services-select2' ),
+ \VERSION,
+ true
+ );
+ }
}
diff --git a/src/Providers/WPCronServiceProvider.php b/src/Providers/WPCronServiceProvider.php
index 42170cf..ef59bd8 100644
--- a/src/Providers/WPCronServiceProvider.php
+++ b/src/Providers/WPCronServiceProvider.php
@@ -22,6 +22,7 @@
use DateTime;
use DateTimeZone;
use OWC\My_Services\WPCron\Events\PopulateInformatieobjecttypen;
+use OWC\My_Services\WPCron\Events\PopulateZaaktypen;
/**
* Register WP-Cron service provider.
@@ -31,6 +32,7 @@
class WPCronServiceProvider extends ServiceProvider
{
private const POPULATE_INFORMATIEOBJECTTYPEN_HOOK = 'owc-mijn-services-populate-informatieobjecttypen-cron';
+ private const POPULATE_ZAAKTYPEN_HOOK = 'owc-mijn-services-populate-zaaktypen-cron';
/**
* @inheritDoc
@@ -42,6 +44,12 @@ public function register(): void
if ( ! wp_next_scheduled( self::POPULATE_INFORMATIEOBJECTTYPEN_HOOK )) {
wp_schedule_event( $this->time_to_execute( 'tomorrow 04:00:00' ), 'daily', self::POPULATE_INFORMATIEOBJECTTYPEN_HOOK );
}
+
+ add_action( self::POPULATE_ZAAKTYPEN_HOOK, ( new PopulateZaaktypen() )->init( ... ) );
+
+ if ( ! wp_next_scheduled( self::POPULATE_ZAAKTYPEN_HOOK )) {
+ wp_schedule_event( $this->time_to_execute( 'tomorrow 04:00:00' ), 'daily', self::POPULATE_ZAAKTYPEN_HOOK );
+ }
}
/**
diff --git a/src/Services/InformatieObjectDownloadService.php b/src/Services/InformatieObjectDownloadService.php
index fe1d0d6..34a400b 100644
--- a/src/Services/InformatieObjectDownloadService.php
+++ b/src/Services/InformatieObjectDownloadService.php
@@ -82,7 +82,8 @@ public function download_file_from_request(): string
return '';
}
- $this->client = apiClientManager()->getClient( $this->supplier_key_to_name( $supplier ) );
+ $supplier_name = $this->supplier_key_to_name( $supplier );
+ $this->client = apiClientManager()->getClient( $supplier_name );
$identification = ZaakIdEncoderDecoder::decode( $identification );
$zaak = $this->validate_zaak( $identification );
@@ -106,10 +107,11 @@ public function download_file_from_request(): string
return '';
}
- $allowed_informatieobjecttypen = (array) ContainerResolver::make()->get( 'display.allowed-informatieobjecttypen' );
+ $allowed_per_supplier = (array) ContainerResolver::make()->get( 'display.allowed-informatieobjecttypen' );
+ $allowed_for_supplier = (array) ( $allowed_per_supplier[ $supplier_name ] ?? array() );
- if ( 0 < count( $allowed_informatieobjecttypen )) {
- if ( ! $this->validate_information_object_to_configured_types( $allowed_informatieobjecttypen, $zaakinformatie_object, $download_identification, $identification )) {
+ if ( 0 < count( $allowed_for_supplier )) {
+ if ( ! $this->validate_information_object_to_configured_types( $allowed_for_supplier, $zaakinformatie_object, $download_identification, $identification )) {
return '';
}
}
@@ -148,14 +150,23 @@ protected function validate_zaak( string $identification ): ?Zaak
$has_kvk_identification = '' !== $this->kvk || '' !== $this->vestigings_nummer || '' !== $this->rsin;
if ($has_kvk_identification && ! ContainerResolver::make()->get( 'display.disable-kvk-filtering' )) {
- $authentication_filter_applied = $this->add_kvk_filter( $filter, $this->rsin, $this->vestigings_nummer, $this->kvk ) || $authentication_filter_applied;
+ $authentication_filter_applied = $this->add_kvk_filter( $filter, $this->rsin, $this->vestigings_nummer ) || $authentication_filter_applied;
}
if ( ! $authentication_filter_applied) {
throw new Exception( 'No valid authentication filter applied to zaken filter.' );
}
- return $this->client->zaken()->filter( $filter )->first() ?: null;
+ $zaak = $this->client->zaken()->expandOnly( self::EXPAND_WITH_ROLLEN )->filter( $filter )->first() ?: null;
+
+ // The betrokkene filters above independently match against the zaak's rollen, so they
+ // don't guarantee it's the same role that is both the betrokkene and the initiator.
+ // Verify that in code instead, now that the rollen are available on the zaak.
+ if ($zaak instanceof Zaak && ! $this->zaak_has_authenticated_initiator( $zaak, $this->bsn, $this->vestigings_nummer, $this->rsin )) {
+ return null;
+ }
+
+ return $zaak;
} catch (Exception $e) {
LoggerService::log_exception( $e, array( 'context' => "Error validating zaak with identification '{$identification}' for informatieobject download." ) );
diff --git a/src/Settings/Adapters/ZaaktypeAdapter.php b/src/Settings/Adapters/ZaaktypeAdapter.php
new file mode 100644
index 0000000..2d3220e
--- /dev/null
+++ b/src/Settings/Adapters/ZaaktypeAdapter.php
@@ -0,0 +1,257 @@
+client = $client;
+ $this->supplier_name = $supplier_name;
+ $this->is_cron = $is_cron;
+ }
+
+ /**
+ * Builds the transient key used to cache the zaaktypen of a supplier.
+ */
+ public static function transient_key( string $supplier_name ): string
+ {
+ return sprintf( 'owcms-zaaktypen-%s', strtolower( $supplier_name ) );
+ }
+
+ /**
+ * Builds the transient key used to cache the zaaktype URL migrations of a supplier.
+ */
+ public static function migrations_transient_key( string $supplier_name ): string
+ {
+ return sprintf( 'owcms-zaaktypen-url-migrations-%s', strtolower( $supplier_name ) );
+ }
+
+ /**
+ * Returns a map of superseded zaaktype URL => current (latest version) zaaktype URL for a
+ * supplier, built the last time its zaaktypen were freshly fetched by handle(). Only reads
+ * the cache, never triggers a live fetch itself, so it's safe to call at render time.
+ *
+ * @return array
+ */
+ public static function url_migrations( string $supplier_name ): array
+ {
+ $migrations = get_transient( self::migrations_transient_key( $supplier_name ) );
+
+ return is_array( $migrations ) ? $migrations : array();
+ }
+
+ /**
+ * Returns the zaaktypen of the supplier as a value => label map,
+ * ready to use as options for a select field.
+ *
+ * When called from a cron context, the transient is always refreshed rather than reused,
+ * so the cache stays warm and no site visitor or editor has to wait for the API request.
+ *
+ * @return array
+ */
+ public function handle(): array
+ {
+ $transient_key = self::transient_key( $this->supplier_name );
+ $types = get_transient( $transient_key );
+
+ if ( ! $this->is_cron ) {
+ return is_array( $types ) ? $types : array();
+ }
+
+ try {
+ $raw = $this->fetch_types();
+ $filtered = $this->filter_zaaktypen_by_version_date( $raw );
+ $types = $this->prepare_types( $filtered );
+ } catch (Exception $e) {
+ LoggerService::log( 'error', $e->getMessage() );
+
+ return array();
+ }
+
+ if (array() === $types) {
+ return array();
+ }
+
+ set_transient( $transient_key, $types, self::TRANSIENT_LIFETIME_IN_SECONDS );
+ set_transient( self::migrations_transient_key( $this->supplier_name ), $this->build_url_migrations( $raw, $filtered ), self::TRANSIENT_LIFETIME_IN_SECONDS );
+
+ return $types;
+ }
+
+ /**
+ * Maps every superseded zaaktype URL (any zaaktype sharing an omschrijving with a newer
+ * version) to the URL of its latest version, so filters that still reference an older
+ * version (e.g. a block's saved attributes) can be resolved to a currently valid zaaktype.
+ *
+ * @param Zaaktype[] $raw
+ * @param Zaaktype[] $filtered
+ * @return array
+ */
+ private function build_url_migrations( array $raw, array $filtered ): array
+ {
+ $latest_url_by_description = array();
+
+ foreach ($filtered as $zaaktype) {
+ $description = trim( (string) ( $zaaktype->omschrijving ?? '' ) );
+ $url = trim( (string) ( $zaaktype->url ?? '' ) );
+
+ if ('' !== $description && '' !== $url) {
+ $latest_url_by_description[ $description ] = $url;
+ }
+ }
+
+ $migrations = array();
+
+ foreach ($raw as $zaaktype) {
+ $description = trim( (string) ( $zaaktype->omschrijving ?? '' ) );
+ $url = trim( (string) ( $zaaktype->url ?? '' ) );
+
+ if ('' === $description || '' === $url) {
+ continue;
+ }
+
+ $latest_url = $latest_url_by_description[ $description ] ?? '';
+
+ if ('' !== $latest_url && $latest_url !== $url) {
+ $migrations[ $url ] = $latest_url;
+ }
+ }
+
+ return $migrations;
+ }
+
+ /**
+ * Filters an array of zaaktypen to ensure unique 'omschrijvingen' (description), keeping
+ * only the one with the latest 'versiedatum' (version date) per description. A zaaktype is
+ * versioned in the ZTC API by creating a new resource with the same omschrijving, so without
+ * this the block would offer several selectable entries that all represent the same zaaktype.
+ */
+ private function filter_zaaktypen_by_version_date( array $zaaktypen ): array
+ {
+ $filtered = array();
+
+ foreach ($zaaktypen as $zaaktype) {
+ $description = trim( (string) ( $zaaktype->omschrijving ?? '' ) );
+
+ if ('' === $description) {
+ continue;
+ }
+
+ if ( ! isset( $filtered[ $description ] )) {
+ $filtered[ $description ] = $zaaktype;
+
+ continue;
+ }
+
+ if ($this->get_zaaktype_timestamp( $zaaktype ) > $this->get_zaaktype_timestamp( $filtered[ $description ] )) {
+ $filtered[ $description ] = $zaaktype;
+ }
+ }
+
+ return array_values( $filtered );
+ }
+
+ private function get_zaaktype_timestamp( Zaaktype $zaaktype ): int
+ {
+ if ( ! $zaaktype->versiedatum instanceof DateTimeImmutable) {
+ return 0;
+ }
+
+ $timestamp = $zaaktype->versiedatum->getTimestamp();
+
+ return false === $timestamp ? 0 : $timestamp;
+ }
+
+ private function fetch_types(): array
+ {
+ $page = 1;
+ $types = array();
+
+ while ($page) {
+ $result = $this->client->zaaktypen()->all( ( new ZaaktypenFilter() )->page( $page ) );
+ $types = array_merge( $types, (array) $result->all() );
+ $page = $result->pageMeta()->getNextPageNumber();
+ }
+
+ return $types;
+ }
+
+ /**
+ * @return array
+ */
+ private function prepare_types( array $types ): array
+ {
+ $prepared = (array) Collection::collect( $types )->map(
+ function ( Zaaktype $zaaktype ) {
+ $description = (string) ( $zaaktype->omschrijving ?? '' );
+ $url = (string) ( $zaaktype->url ?? '' );
+ $identificatie = (string) ( $zaaktype->identificatie ?? '' );
+
+ if ('' === $description || '' === $url) {
+ return null;
+ }
+
+ return array(
+ 'url' => $url,
+ 'label' => '' !== $identificatie ? sprintf( '%s (%s)', $description, $identificatie ) : $description,
+ );
+ }
+ )->filter(
+ function ( $item ) {
+ return null !== $item;
+ }
+ )->all();
+
+ usort(
+ $prepared,
+ function ( $a, $b ) {
+ return strcmp( $a['label'], $b['label'] );
+ }
+ );
+
+ $options = array();
+
+ foreach ($prepared as $item) {
+ $options[ $item['url'] ] = $item['label'];
+ }
+
+ return $options;
+ }
+}
diff --git a/src/Settings/InformatieobjecttypeCatalog.php b/src/Settings/InformatieobjecttypeCatalog.php
new file mode 100644
index 0000000..61f68a4
--- /dev/null
+++ b/src/Settings/InformatieobjecttypeCatalog.php
@@ -0,0 +1,111 @@
+ label map.
+ *
+ * @return array
+ */
+ public function for_supplier( string $supplier_name ): array
+ {
+ try {
+ $client = apiClientManager()->getClient( $supplier_name );
+ } catch (Exception $e) {
+ LoggerService::log( 'error', $e->getMessage() );
+
+ return array();
+ }
+
+ if ( ! $client->supports( 'informatieobjecttypen' )) {
+ return array();
+ }
+
+ return ( new InformatieobjecttypeAdapter( $client, $supplier_name ) )->handle();
+ }
+
+ /**
+ * Returns the informatieobjecttypen of all configured suppliers, grouped per supplier.
+ *
+ * @return array>
+ */
+ public function per_supplier(): array
+ {
+ $catalog = array();
+
+ foreach ($this->get_configured_suppliers() as $supplier) {
+ $types = $this->for_supplier( $supplier['name'] );
+
+ if (array() === $types) {
+ continue;
+ }
+
+ $catalog[ $supplier['name'] ] = $types;
+ }
+
+ return $catalog;
+ }
+
+ /**
+ * Splits a flat list of allowed informatieobjecttype urls into the suppliers that actually
+ * own each url, so filtering can be scoped per supplier instead of applied globally.
+ *
+ * @param string[] $allowed_urls
+ * @return array
+ */
+ public function group_by_supplier( array $allowed_urls ): array
+ {
+ if (array() === $allowed_urls) {
+ return array();
+ }
+
+ $grouped = array();
+
+ foreach ($this->per_supplier() as $supplier_name => $types) {
+ $matching = array_values( array_intersect( $allowed_urls, array_keys( $types ) ) );
+
+ if (array() !== $matching) {
+ $grouped[ $supplier_name ] = $matching;
+ }
+ }
+
+ return $grouped;
+ }
+}
diff --git a/src/Settings/OptionsPageRegistrar.php b/src/Settings/OptionsPageRegistrar.php
index 1b9dd9e..fec5838 100644
--- a/src/Settings/OptionsPageRegistrar.php
+++ b/src/Settings/OptionsPageRegistrar.php
@@ -19,15 +19,12 @@
exit;
}
-use Exception;
use OWC\My_Services\CMB2\SelectOptgroupType;
use OWC\My_Services\Controllers\InformatieobjecttypenCacheController;
+use OWC\My_Services\Controllers\ZaaktypenCacheController;
use OWC\My_Services\Services\LoggerService;
-use OWC\My_Services\Settings\Adapters\InformatieobjecttypeAdapter;
use OWC\My_Services\Traits\Supplier;
-use function OWC\ZGW\apiClientManager;
-
/**
* Options page registrar for settings.
*
@@ -48,16 +45,7 @@ public function addSettingsFields(): void
return;
}
- /**
- * Filters the capability required to manage the 'Toegestane informatieobjecttypen' setting.
- *
- * Defaults to 'manage_options', in which case only administrators can see or edit the
- * setting. Filtering this to a different capability additionally exposes the settings
- * page (and only this one field on it) to any user with that capability.
- *
- * @since 0.11.0
- */
- $allowed_informatieobjecttypen_capability = apply_filters( 'owcms::settings/allowed_informatieobjecttypen_capability', 'manage_options' );
+ $allowed_settings_capability = self::get_allowed_settings_capability();
$options = new_cmb2_box(
array(
@@ -67,7 +55,7 @@ public function addSettingsFields(): void
'option_key' => 'owc_mijn_services_settings',
'parent_slug' => 'options-general.php',
- 'capability' => $allowed_informatieobjecttypen_capability,
+ 'capability' => $allowed_settings_capability,
)
);
@@ -75,6 +63,10 @@ public function addSettingsFields(): void
return current_user_can( 'manage_options' );
};
+ $allowed_settings_show_on_cb = function () use ( $allowed_settings_capability ) {
+ return current_user_can( 'manage_options' ) || current_user_can( $allowed_settings_capability );
+ };
+
$options->add_field(
array(
'name' => __( 'Logboekinstellingen', 'owc-mijn-services' ),
@@ -107,7 +99,6 @@ public function addSettingsFields(): void
'desc' => __( 'Selecteer de informatieobjecttypen die mogen worden gebruikt voor het ophalen van informatieobjecten bij een zaak. Laat dit veld leeg om alle informatieobjecten te tonen.', 'owc-mijn-services' ),
'id' => 'owc-mijn-services-allowed-informatieobjecttypen',
'type' => 'select',
- 'repeatable' => true,
'render_class' => SelectOptgroupType::class,
'options_cb' => function () {
return $this->get_informatieobjecttype_options();
@@ -116,35 +107,53 @@ public function addSettingsFields(): void
( new InformatieobjecttypenCacheController() )->render_clear_cache_button();
},
'attributes' => array(
- 'style' => 'width: 100%;',
- ),
- 'text' => array(
- 'add_row_text' => __( 'Informatieobjecttype toevoegen', 'owc-mijn-services' ),
+ 'class' => 'cmb2_select owcms-informatieobjecttypen-select',
+ 'style' => 'width: 100%;',
+ 'data-placeholder' => __( 'Zoek en selecteer informatieobjecttypen…', 'owc-mijn-services' ),
),
- 'show_on_cb' => function () use ( $allowed_informatieobjecttypen_capability ) {
- return current_user_can( 'manage_options' ) || current_user_can( $allowed_informatieobjecttypen_capability );
+ 'show_on_cb' => $allowed_settings_show_on_cb,
+ )
+ );
+
+ $options->add_field(
+ array(
+ 'name' => __( 'Zaaktypefiltering ondersteunde leveranciers', 'owc-mijn-services' ),
+ 'desc' => __( 'Selecteer de leveranciers waarvan de Zaken API filteren op zaaktype ondersteunt. Alleen voor geselecteerde leveranciers wordt gefilterd op de zaaktypen die in het \'Mijn zaken\'-blok zijn geselecteerd; voor niet geselecteerde leveranciers worden altijd alle zaken opgehaald.', 'owc-mijn-services' ),
+ 'id' => 'owc-mijn-services-zaaktype-filtering-suppliers',
+ 'type' => 'multicheck',
+ 'select_all_button' => false,
+ 'options_cb' => function () {
+ return $this->get_supplier_options();
+ },
+ 'sanitization_cb' => function ( $value ) {
+ return is_array( $value ) ? array_values( array_filter( $value, 'is_string' ) ) : array();
},
+ 'show_on_cb' => $allowed_settings_show_on_cb,
)
);
$options->add_field(
array(
- 'name' => __( 'Productiecontroles uitschakelen', 'owc-mijn-services' ),
- 'desc' => __( 'Schakel deze optie in om de verplichting van het gebruik van de blokattributen \'Filter op BSN\' of \'Filter op KVK\' uit te zetten. Standaard zijn productiecontroles ingeschakeld en is minimaal één van beide filterattributen vereist.', 'owc-mijn-services' ),
- 'id' => 'owc-mijn-services-disable-production-checks',
- 'type' => 'checkbox',
- 'sanitization_cb' => function ( $value ) {
- return $this->handle_unchecked_checkbox( $value );
+ 'name' => '', // Left blank to avoid duplicating the label_cb heading below.
+ 'desc' => __( 'De zaaktypen die gebruikt worden voor de zaaktypefilter in het \'Mijn zaken\'-blok worden dagelijks automatisch ververst. Gebruik onderstaande knop om ze direct opnieuw op te halen.', 'owc-mijn-services' ),
+ 'id' => 'owc-mijn-services-zaaktypen-cache',
+ 'type' => 'title',
+ 'label_cb' => function () {
+ // 'title' fields have no label_cb by default, so restore it to match other rows.
+ return sprintf( '%s ', esc_html__( 'Zaaktypen', 'owc-mijn-services' ) );
},
- 'show_on_cb' => $admin_only_show_on_cb,
+ 'after' => function () {
+ ( new ZaaktypenCacheController() )->render_refetch_button();
+ },
+ 'show_on_cb' => $allowed_settings_show_on_cb,
)
);
$options->add_field(
array(
- 'name' => __( 'Filtering op KVK uitschakelen', 'owc-mijn-services' ),
- 'desc' => __( 'Schakel deze optie in als de leverancier het filteren op KVK (eHerkenning) niet ondersteunt. Gebruikers die via eHerkenning zijn ingelogd kunnen dan geen zaken ophalen.', 'owc-mijn-services' ),
- 'id' => 'owc-mijn-services-disable-kvk-filtering',
+ 'name' => __( 'Productiecontroles uitschakelen', 'owc-mijn-services' ),
+ 'desc' => __( 'Schakel deze optie in om de verplichting van het gebruik van de blokattributen \'Filter op BSN\' of \'Filter op KVK\' uit te zetten. Standaard zijn productiecontroles ingeschakeld en is minimaal één van beide filterattributen vereist.', 'owc-mijn-services' ),
+ 'id' => 'owc-mijn-services-disable-production-checks',
'type' => 'checkbox',
'sanitization_cb' => function ( $value ) {
return $this->handle_unchecked_checkbox( $value );
@@ -155,9 +164,9 @@ public function addSettingsFields(): void
$options->add_field(
array(
- 'name' => __( 'Uitgebreide KVK-filtering inschakelen', 'owc-mijn-services' ),
- 'desc' => __( 'Schakel deze optie in om bij het filteren op KVK (eHerkenning) ook te filteren op RSIN of vestigingsnummer, indien beschikbaar. Niet elke leverancier ondersteunt deze filterparameters.', 'owc-mijn-services' ),
- 'id' => 'owc-mijn-services-enable-extended-kvk-filtering',
+ 'name' => __( 'Filtering op KVK uitschakelen', 'owc-mijn-services' ),
+ 'desc' => __( 'Schakel deze optie in als de leverancier het filteren op KVK (eHerkenning) niet ondersteunt. Gebruikers die via eHerkenning zijn ingelogd kunnen dan geen zaken ophalen.', 'owc-mijn-services' ),
+ 'id' => 'owc-mijn-services-disable-kvk-filtering',
'type' => 'checkbox',
'sanitization_cb' => function ( $value ) {
return $this->handle_unchecked_checkbox( $value );
@@ -175,7 +184,7 @@ public function addSettingsFields(): void
'sanitization_cb' => function ( $value ) {
return $this->handle_unchecked_checkbox( $value );
},
- 'show_on_cb' => $admin_only_show_on_cb,
+ 'show_on_cb' => $allowed_settings_show_on_cb,
)
);
@@ -188,7 +197,7 @@ public function addSettingsFields(): void
'sanitization_cb' => function ( $value ) {
return $this->handle_unchecked_checkbox( $value );
},
- 'show_on_cb' => $admin_only_show_on_cb,
+ 'show_on_cb' => $allowed_settings_show_on_cb,
)
);
}
@@ -197,35 +206,27 @@ public function addSettingsFields(): void
* Builds the list of informatieobjecttypen of all configured suppliers, grouped per supplier,
* to use as options for the SelectOptgroupType field.
*
- * @return array>
+ * @return array>
* @since 0.12.0
*/
private function get_informatieobjecttype_options(): array
{
- $options = array(
- '' => __( 'Selecteer een informatieobjecttype', 'owc-mijn-services' ),
- );
-
- foreach ($this->get_configured_suppliers() as $supplier) {
- try {
- $client = apiClientManager()->getClient( $supplier['name'] );
- } catch (Exception $e) {
- LoggerService::log( 'error', $e->getMessage() );
-
- continue;
- }
-
- if ( ! $client->supports( 'informatieobjecttypen' )) {
- continue;
- }
-
- $types = ( new InformatieobjecttypeAdapter( $client, $supplier['name'] ) )->handle();
+ return ( new InformatieobjecttypeCatalog() )->per_supplier();
+ }
- if (array() === $types) {
- continue;
- }
+ /**
+ * Builds the list of configured suppliers as a name => name map, to use as options for the
+ * 'Zaaktypefiltering ondersteunde leveranciers' multicheck field.
+ *
+ * @return array
+ * @since 0.17.0
+ */
+ private function get_supplier_options(): array
+ {
+ $options = array();
- $options[ $supplier['name'] ] = $types;
+ foreach ($this->get_configured_suppliers() as $supplier) {
+ $options[ $supplier['name'] ] = $supplier['name'];
}
return $options;
@@ -239,4 +240,41 @@ private function handle_unchecked_checkbox( mixed $value ): ?string
return $value;
}
+
+ /**
+ * Resolves the capability required to manage the non-administrator-only settings.
+ *
+ * @since 0.15.0
+ */
+ public static function get_allowed_settings_capability(): string
+ {
+ /**
+ * Filters the capability required to manage the non-administrator-only settings.
+ *
+ * Defaults to 'manage_options', in which case only administrators can see or edit these
+ * settings. Filtering this to a different capability additionally exposes the settings
+ * page (and only these fields on it) to any user with that capability.
+ *
+ * @since 0.15.0
+ */
+ $capability = apply_filters( 'owcms::settings/allowed_settings_capability', 'manage_options' );
+
+ if ( has_filter( 'owcms::settings/allowed_informatieobjecttypen_capability' ) ) {
+ /**
+ * Deprecated: use the `owcms::settings/allowed_settings_capability` filter instead,
+ * which now also governs the 'Statusstappen zonder datum verbergen' and
+ * 'Volgnummers verbergen' settings.
+ *
+ * @deprecated 0.15.0 Use `owcms::settings/allowed_settings_capability` instead.
+ */
+ $capability = apply_filters_deprecated(
+ 'owcms::settings/allowed_informatieobjecttypen_capability',
+ array( $capability ),
+ '0.15.0',
+ 'owcms::settings/allowed_settings_capability'
+ );
+ }
+
+ return $capability;
+ }
}
diff --git a/src/Traits/AuthenticationFilter.php b/src/Traits/AuthenticationFilter.php
index 7acabc0..167f7d0 100644
--- a/src/Traits/AuthenticationFilter.php
+++ b/src/Traits/AuthenticationFilter.php
@@ -21,6 +21,9 @@
use OWC\My_Services\ContainerResolver;
use OWC\ZGW\Endpoints\Filter\ZakenFilter;
+use OWC\ZGW\Entities\Attributes\SubjectType;
+use OWC\ZGW\Entities\Rol;
+use OWC\ZGW\Entities\Zaak;
/**
* Authentication filter trait.
@@ -29,35 +32,84 @@
*/
trait AuthenticationFilter
{
+ /**
+ * The expand resources needed to verify, without extra API calls, that a single
+ * zaak actually belongs to the authenticated user. Include on top of the resources
+ * already expanded by default (currently 'zaaktype' and 'status').
+ *
+ * @since 0.15.0
+ */
+ protected const EXPAND_WITH_ROLLEN = array( 'zaaktype', 'status', 'rollen' );
+
/**
* Applies exactly one KVK-based identification filter, preferring vestigingsNummer, then
* RSIN, then the plain kvkNummer, so only one is ever active.
*
- * RSIN and vestigingsNummer are not supported by every supplier, so they are
- * only used when explicitly enabled via the 'display.enable-extended-kvk-filtering' setting.
+ * The plain kvkNummer filter is not supported by every supplier and is deprecated,
+ * so it is no longer used.
*/
- protected function add_kvk_filter( ZakenFilter $filter, string $rsin, string $vestigings_nummer, string $kvk ): bool
+ protected function add_kvk_filter( ZakenFilter $filter, string $rsin, string $vestigings_nummer ): bool
{
- $extended_filtering_enabled = (bool) ContainerResolver::make()->get( 'display.enable-extended-kvk-filtering' );
-
- if ($extended_filtering_enabled && '' !== $vestigings_nummer) {
+ if ('' !== $vestigings_nummer) {
$filter->add( 'rol__betrokkeneIdentificatie__vestiging__vestigingsNummer', $vestigings_nummer );
return true;
}
- if ($extended_filtering_enabled && '' !== $rsin) {
+ if ('' !== $rsin) {
$filter->add( 'rol__betrokkeneIdentificatie__nietNatuurlijkPersoon__innNnpId', $rsin );
return true;
}
- if ('' !== $kvk) {
- $filter->add( 'rol__betrokkeneIdentificatie__vestiging__kvkNummer', $kvk );
+ return false;
+ }
+ /**
+ * Verifies that the given zaak has a role with 'omschrijvingGeneriek' 'initiator' whose
+ * betrokkene identification matches the authenticated user.
+ *
+ * The query filters used to retrieve a single zaak (byBsn/add_kvk_filter combined with
+ * a 'rol__omschrijvingGeneriek' filter) each match against the zaak's rollen independently,
+ * so they don't guarantee both conditions are satisfied by the same role. This performs
+ * that check in code instead, using the same identification precedence as add_kvk_filter,
+ * against roles already available on the zaak (see EXPAND_WITH_ROLLEN).
+ *
+ * @since NEXT
+ */
+ protected function zaak_has_authenticated_initiator( Zaak $zaak, string $bsn, string $vestigings_nummer, string $rsin ): bool
+ {
+ if ('' !== $bsn && $zaak->isInitiatedBy( $bsn )) {
return true;
}
+ if ( (bool) ContainerResolver::make()->get( 'display.disable-kvk-filtering' )) {
+ return false;
+ }
+
+ if ('' !== $vestigings_nummer) {
+ return $this->zaak_has_initiator_role( $zaak, SubjectType::VESTIGING, 'vestigingsNummer', $vestigings_nummer );
+ }
+
+ if ('' !== $rsin) {
+ return $this->zaak_has_initiator_role( $zaak, SubjectType::NIET_NATUURLIJK_PERSOON, 'innNnpId', $rsin );
+ }
+
return false;
}
+
+ /**
+ * @since 0.15.0
+ */
+ private function zaak_has_initiator_role( Zaak $zaak, SubjectType $betrokkene_type, string $identificatie_key, string $identificatie_value ): bool
+ {
+ return $zaak->rollen->filter(
+ function ( Rol $rol ) use ( $betrokkene_type, $identificatie_key, $identificatie_value ) {
+ // ZGW entity property names below follow the ZGW API spec, not our naming convention.
+ return $rol->isInitiator()
+ && $rol->betrokkeneType?->is( $betrokkene_type ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ && ( ( (array) $rol->betrokkeneIdentificatie )[ $identificatie_key ] ?? null ) === $identificatie_value; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ }
+ )->isNotEmpty();
+ }
}
diff --git a/src/Traits/Supplier.php b/src/Traits/Supplier.php
index 35b7e07..b0f63dd 100644
--- a/src/Traits/Supplier.php
+++ b/src/Traits/Supplier.php
@@ -19,6 +19,8 @@
exit;
}
+use OWC\My_Services\ContainerResolver;
+
/**
* Supplier trait.
*
@@ -59,4 +61,19 @@ function ( $client ) {
return array_values( $clients );
}
+
+ /**
+ * Whether filtering zaken on zaaktype is supported for the given supplier. Governed by the
+ * 'Zaaktypefiltering ondersteunde leveranciers' setting, since not every supplier's Zaken API
+ * supports filtering on the zaaktype parameter. Disabled by default; a supplier must be
+ * explicitly selected in the setting before zaaktype filtering is applied for it.
+ *
+ * @since 0.17.0
+ */
+ public function supports_zaaktype_filtering( string $supplier_name ): bool
+ {
+ $supported = (array) ContainerResolver::make()->get( 'display.zaaktype-filtering-suppliers' );
+
+ return in_array( $supplier_name, $supported, true );
+ }
}
diff --git a/src/Views/owc-error.blade.php b/src/Views/owc-error.blade.php
index 9b14d95..4b3981a 100644
--- a/src/Views/owc-error.blade.php
+++ b/src/Views/owc-error.blade.php
@@ -13,8 +13,8 @@
@endphp
@if (isset($message))
-
-
{{ $header_text }}
-
{{ $message }}
+
+
{{ $header_text }}
+
{{ $message }}
@endif
diff --git a/src/Views/owc-overview-zaken.blade.php b/src/Views/owc-overview-zaken.blade.php
index 69548bd..310c58b 100644
--- a/src/Views/owc-overview-zaken.blade.php
+++ b/src/Views/owc-overview-zaken.blade.php
@@ -8,17 +8,26 @@
exit();
}
- $tabs = array_values(array_filter([
+ $show_tabs = $show_tabs ?? true;
+ $all_zaken = $all_zaken ?? [];
+
+ $tabs = [
[
- 'label' => 'Lopende zaken',
+ 'label' => __('Lopende zaken', 'owc-mijn-services'),
// Uses partials/nlds/denhaag/card to render each card.
- 'cards' => $current_zaken,
+ 'cards' => $current_zaken,
+ 'emptyMessage' => __('U heeft op dit moment geen lopende zaken.', 'owc-mijn-services'),
],
- [
- 'label' => 'Afgeronde zaken',
+ [
+ 'label' => __('Afgeronde zaken', 'owc-mijn-services'),
'cards' => $completed_zaken,
+ 'emptyMessage' => __('U heeft nog geen afgeronde zaken.', 'owc-mijn-services'),
],
- ], fn (array $tab): bool => !empty($tab['cards'])));
+ ];
@endphp
-
+@if ($show_tabs)
+
+@else
+
+@endif
diff --git a/src/Views/partials/nlds/denhaag/status.blade.php b/src/Views/partials/nlds/denhaag/status.blade.php
index eb6f435..1a163b3 100644
--- a/src/Views/partials/nlds/denhaag/status.blade.php
+++ b/src/Views/partials/nlds/denhaag/status.blade.php
@@ -11,48 +11,6 @@
$steps ??= [];
$collapsible ??= true;
- // Example steps data structure with sub steps.
- // $steps = [
- // [
- // 'id' => 'step-1',
- // 'title' => 'Stap 1: Aanvraag ingediend',
- // 'status' => 'checked',
- // 'date' => '6 januari 2025',
- // 'meta' => 'Door: John Doe',
- // ],
- // [
- // 'id' => 'step-2',
- // 'title' => 'Stap 2: Controle gegevens',
- // 'status' => 'current',
- // 'date' => '16 januari 2025',
- // 'steps' => [
- // [
- // 'id' => 'step-2a',
- // 'title' => 'Substap 2a: Controle adres',
- // 'status' => 'checked',
- // 'date' => '20 januari 2025',
- // ],
- // [
- // 'id' => 'step-2b',
- // 'title' => 'Substap 2b: Controle identiteitsbewijs',
- // 'status' => 'current',
- // ],
- // ],
- // ],
- // [
- // 'id' => 'step-3',
- // 'title' => 'Stap 3: Goedkeuring',
- // 'status' => 'not-checked',
- // 'date' => '6 februari 2025',
- // ],
- // [
- // 'id' => 'step-4',
- // 'title' => 'Stap 4: Afronden',
- // 'status' => 'not-checked',
- // 'date' => '6 februari 2025',
- // ],
- // ];
-
@endphp
$zaak->registerDate('j F Y'),
'Startdatum' => $zaak->startDate('j F Y'),
+ 'Verlenging' => $zaak->extensionFormatted(),
+ 'Opschorting' => $zaak->suspensionReason(),
'Einddatum gepland' => $zaak->endDatePlanned() && empty($zaak->endDate()) ? $zaak->endDatePlanned() : null,
'Einddatum' => $zaak->endDate(),
'Zaaknummer' => $zaak->getValue('identificatie', ''),
diff --git a/src/Views/partials/owc-single-zaak-documents.blade.php b/src/Views/partials/owc-single-zaak-documents.blade.php
index e6618c8..0054d79 100644
--- a/src/Views/partials/owc-single-zaak-documents.blade.php
+++ b/src/Views/partials/owc-single-zaak-documents.blade.php
@@ -22,8 +22,13 @@
$name = $object->getValue('bestandsnaam', '');
$href = $object->downloadUrl($zaak->getValue('identificatie', ''), $zaak->getValue('supplier', ''));
- $size = $object->formattedMetaData() ?: null;
+ $size = $object->sizeFormatted() ?: null;
$lastUpdated = $object->getValue('lastUpdated', null);
+ $creationDate = $object->getValue('creatiedatum', null);
+
+ if (null === $lastUpdated && $creationDate instanceof DateTimeImmutable) {
+ $lastUpdated = $creationDate->format('Y-m-d');
+ }
@endphp
@include('partials.nlds.denhaag.file', [
diff --git a/src/Views/partials/owc-single-zaak-status.blade.php b/src/Views/partials/owc-single-zaak-status.blade.php
index 8b2c6e1..d2929b3 100644
--- a/src/Views/partials/owc-single-zaak-status.blade.php
+++ b/src/Views/partials/owc-single-zaak-status.blade.php
@@ -25,6 +25,9 @@
}
}
+ // The next step is only marked as current once the current step is actually checked (has a date).
+ $currentStepChecked = false;
+
foreach ($steps as $step) {
$statusUpdate = null;
@@ -40,11 +43,12 @@
}
// Determine step status
- if ($step->isPast()) {
+ if ($step->isPast() && $statusUpdate) {
$status = 'checked';
- } elseif ($step->isCurrent()) {
+ } elseif ($step->isCurrent() && $statusUpdate) {
$status = 'checked';
- } elseif ($step->isNext()) {
+ $currentStepChecked = true;
+ } elseif ($step->isNext() && $currentStepChecked) {
$status = 'current';
} else {
$status = 'not-checked';
diff --git a/src/WPCron/Events/PopulateZaaktypen.php b/src/WPCron/Events/PopulateZaaktypen.php
new file mode 100644
index 0000000..1986335
--- /dev/null
+++ b/src/WPCron/Events/PopulateZaaktypen.php
@@ -0,0 +1,62 @@
+get_configured_suppliers() as $supplier) {
+ $this->populate_for_supplier( $supplier );
+ }
+ }
+
+ private function populate_for_supplier( array $supplier ): void
+ {
+ try {
+ $client = apiClientManager()->getClient( $supplier['name'] );
+ } catch (Exception $e) {
+ LoggerService::log( 'error', sprintf( 'Error initializing API client for supplier %s: %s', $supplier['name'], $e->getMessage() ) );
+
+ return;
+ }
+
+ if ( ! $client->supports( 'zaaktypen' )) {
+ return;
+ }
+
+ ( new ZaaktypeAdapter( client: $client, supplier_name: $supplier['name'], is_cron: true ) )->handle();
+ }
+}
diff --git a/webpack.scss.config.js b/webpack.scss.config.js
index 197f44a..3caa683 100644
--- a/webpack.scss.config.js
+++ b/webpack.scss.config.js
@@ -1,9 +1,11 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
entry: {
- editor: './resources/scss/editor.scss'
+ editor: './resources/scss/editor.scss',
+ 'informatieobjecttypen-select': './resources/scss/admin/informatieobjecttypen-select.scss'
},
output: {
path: path.resolve(process.cwd(), 'build/css'),
@@ -20,5 +22,14 @@ module.exports = {
new MiniCssExtractPlugin({
filename: '[name].css',
}),
+ new CopyWebpackPlugin({
+ patterns: [
+ {
+ from: path.resolve(process.cwd(), 'resources/vendor/select2'),
+ to: path.resolve(process.cwd(), 'build/select2'),
+ info: { minimized: true },
+ },
+ ],
+ }),
],
};