diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cbab857e67..bcd47e7633 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v7.0.1 - name: Set up Python - uses: actions/setup-python@v6.3.0 + uses: actions/setup-python@v7.0.0 with: python-version: "3.13" - name: Install dependencies diff --git a/.github/workflows/lint-and-analyse-php.yml b/.github/workflows/lint-and-analyse-php.yml index 9d4b84da0c..e2db00d093 100644 --- a/.github/workflows/lint-and-analyse-php.yml +++ b/.github/workflows/lint-and-analyse-php.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v7.0.1 with: fetch-depth: 0 - - uses: actions/setup-python@v6.3.0 + - uses: actions/setup-python@v7.0.0 with: python-version: "3.13" - run: pip install --upgrade tox diff --git a/Presenters/Admin/ManageConfigurationPresenter.php b/Presenters/Admin/ManageConfigurationPresenter.php index 38757d42dd..04e18e6019 100644 --- a/Presenters/Admin/ManageConfigurationPresenter.php +++ b/Presenters/Admin/ManageConfigurationPresenter.php @@ -224,6 +224,10 @@ public function Update() } } + if (!$isPluginConfig) { + $mergedSettings = $this->EnableIcsSubscriptionIfNeeded($mergedSettings); + } + Log::Debug('Saving %s settings', count($configSettings)); // WriteSettings will handle the proper nesting based on file type @@ -242,6 +246,42 @@ public function SetHomepage() ServiceLocator::GetDatabase()->Execute($command); } + /** + * Generates and stores an ICS subscription key when the admin enables ICS + * through this settings page without setting a key, so subscription links + * do not remain broken until an unrelated schedule/resource/user toggle + * triggers key generation. + * + * @param array $mergedSettings + * @return array + */ + private function EnableIcsSubscriptionIfNeeded(array $mergedSettings): array + { + if (!isset($mergedSettings['ics']) || !is_array($mergedSettings['ics'])) { + return $mergedSettings; + } + + if (ConfigKeys::hasEnv(ConfigKeys::ICS_SUBSCRIPTION_KEY)) { + return $mergedSettings; + } + + $icsEnabled = filter_var( + $mergedSettings['ics']['enabled'] ?? ConfigKeys::ICS_ENABLED['default'], + FILTER_VALIDATE_BOOLEAN + ); + + $newKey = ConfigurationFile::GenerateSubscriptionKeyIfNeeded( + $icsEnabled, + $mergedSettings['ics']['subscription.key'] ?? '' + ); + + if ($newKey !== null) { + $mergedSettings['ics']['subscription.key'] = $newKey; + } + + return $mergedSettings; + } + private function ShouldBeSkipped(string $key, ?ConfigKey $meta): bool { if ($meta === null) { diff --git a/Web/assets/vendor/datatables-buttons/2.4.2/css/buttons.bootstrap5.min.css b/Web/assets/vendor/datatables-buttons/2.4.2/css/buttons.bootstrap5.min.css deleted file mode 100644 index 3229c89006..0000000000 --- a/Web/assets/vendor/datatables-buttons/2.4.2/css/buttons.bootstrap5.min.css +++ /dev/null @@ -1 +0,0 @@ -@keyframes dtb-spinner{100%{transform:rotate(360deg)}}@-o-keyframes dtb-spinner{100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes dtb-spinner{100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dtb-spinner{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes dtb-spinner{100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}div.dataTables_wrapper{position:relative}div.dt-buttons{position:initial}div.dt-buttons .dt-button{overflow:hidden;text-overflow:ellipsis}div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border-radius:.75em;box-shadow:3px 4px 10px 1px rgba(0, 0, 0, 0.8);text-align:center;z-index:2003;overflow:hidden}div.dt-button-info h2{padding:2rem 2rem 1rem 2rem;margin:0;font-weight:normal}div.dt-button-info>div{padding:1em 2em 2em 2em}div.dtb-popover-close{position:absolute;top:6px;right:6px;width:22px;height:22px;text-align:center;border-radius:3px;cursor:pointer;z-index:2003}button.dtb-hide-drop{display:none !important}div.dt-button-collection-title{text-align:center;padding:.3em 0 .5em;margin-left:.5em;margin-right:.5em;font-size:.9em}div.dt-button-collection-title:empty{display:none}span.dt-button-spacer{display:inline-block;margin:.5em;white-space:nowrap}span.dt-button-spacer.bar{border-left:1px solid rgba(0, 0, 0, 0.3);vertical-align:middle;padding-left:.5em}span.dt-button-spacer.bar:empty{height:1em;width:1px;padding-left:0}div.dt-button-collection .dt-button-active{padding-right:3em}div.dt-button-collection .dt-button-active:after{position:absolute;top:50%;margin-top:-10px;right:1em;display:inline-block;content:"✓";color:inherit}div.dt-button-collection .dt-button-active.dt-button-split{padding-right:0}div.dt-button-collection .dt-button-active.dt-button-split:after{display:none}div.dt-button-collection .dt-button-active.dt-button-split>*:first-child{padding-right:3em}div.dt-button-collection .dt-button-active.dt-button-split>*:first-child:after{position:absolute;top:50%;margin-top:-10px;right:1em;display:inline-block;content:"✓";color:inherit}div.dt-button-collection .dt-button-active-a a{padding-right:3em}div.dt-button-collection .dt-button-active-a a:after{position:absolute;right:1em;display:inline-block;content:"✓";color:inherit}div.dt-button-collection span.dt-button-spacer{width:100%;font-size:.9em;text-align:center;margin:.5em 0}div.dt-button-collection span.dt-button-spacer:empty{height:0;width:100%}div.dt-button-collection span.dt-button-spacer.bar{border-left:none;border-bottom:1px solid rgba(0, 0, 0, 0.1);padding-left:0}html.dark div.dt-button-info{background-color:var(--dt-html-background);border:1px solid rgba(255, 255, 255, 0.15)}div.dt-buttons div.btn-group{position:initial}div.dt-buttons div.dropdown-menu{margin-top:4px}div.dt-buttons div.dropdown-menu .dt-button{position:relative}div.dt-buttons div.dropdown-menu div.dt-button-split{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start;align-items:stretch}div.dt-buttons div.dropdown-menu div.dt-button-split a:first-child{min-width:auto;flex:1 0 50px;padding-right:0}div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child{min-width:33px;flex:0;background:transparent;border:none;line-height:1rem;color:var(--bs-dropdown-link-color);padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x)}div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:after{position:relative;left:-3px}div.dt-buttons div.dropdown-menu.fixed{position:fixed;display:block;top:50%;left:50%;margin-left:-75px;border-radius:5px;background-color:white;padding:.5em}div.dt-buttons div.dropdown-menu.fixed.two-column{margin-left:-200px}div.dt-buttons div.dropdown-menu.fixed.three-column{margin-left:-225px}div.dt-buttons div.dropdown-menu.fixed.four-column{margin-left:-300px}div.dt-buttons div.dropdown-menu.fixed.columns{margin-left:-409px}@media screen and (max-width: 1024px){div.dt-buttons div.dropdown-menu.fixed.columns{margin-left:-308px}}@media screen and (max-width: 640px){div.dt-buttons div.dropdown-menu.fixed.columns{margin-left:-203px}}@media screen and (max-width: 460px){div.dt-buttons div.dropdown-menu.fixed.columns{margin-left:-100px}}div.dt-buttons div.dropdown-menu.fixed>:last-child{max-height:100vh;overflow:auto}div.dt-buttons div.dropdown-menu.two-column>:last-child,div.dt-buttons div.dropdown-menu.three-column>:last-child,div.dt-buttons div.dropdown-menu.four-column>:last-child{display:block !important;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}div.dt-buttons div.dropdown-menu.two-column>:last-child>*,div.dt-buttons div.dropdown-menu.three-column>:last-child>*,div.dt-buttons div.dropdown-menu.four-column>:last-child>*{-webkit-column-break-inside:avoid;break-inside:avoid}div.dt-buttons div.dropdown-menu.two-column{width:400px}div.dt-buttons div.dropdown-menu.two-column>:last-child{padding-bottom:1px;column-count:2}div.dt-buttons div.dropdown-menu.three-column{width:450px}div.dt-buttons div.dropdown-menu.three-column>:last-child{padding-bottom:1px;column-count:3}div.dt-buttons div.dropdown-menu.four-column{width:600px}div.dt-buttons div.dropdown-menu.four-column>:last-child{padding-bottom:1px;column-count:4}div.dt-buttons div.dropdown-menu .dt-button{border-radius:0}div.dt-buttons div.dropdown-menu.columns{width:auto}div.dt-buttons div.dropdown-menu.columns>:last-child{display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;gap:6px;width:818px;padding-bottom:1px}div.dt-buttons div.dropdown-menu.columns>:last-child .dt-button{min-width:200px;flex:0 1;margin:0}div.dt-buttons div.dropdown-menu.columns.dtb-b3>:last-child,div.dt-buttons div.dropdown-menu.columns.dtb-b2>:last-child,div.dt-buttons div.dropdown-menu.columns.dtb-b1>:last-child{justify-content:space-between}div.dt-buttons div.dropdown-menu.columns.dtb-b3 .dt-button{flex:1 1 32%}div.dt-buttons div.dropdown-menu.columns.dtb-b2 .dt-button{flex:1 1 48%}div.dt-buttons div.dropdown-menu.columns.dtb-b1 .dt-button{flex:1 1 100%}@media screen and (max-width: 1024px){div.dt-buttons div.dropdown-menu.columns>:last-child{width:612px}}@media screen and (max-width: 640px){div.dt-buttons div.dropdown-menu.columns>:last-child{width:406px}div.dt-buttons div.dropdown-menu.columns.dtb-b3 .dt-button{flex:0 1 32%}}@media screen and (max-width: 460px){div.dt-buttons div.dropdown-menu.columns>:last-child{width:200px}}div.dt-buttons span.dt-button-spacer.empty{margin:1px}div.dt-buttons span.dt-button-spacer.bar:empty{height:inherit}div.dt-buttons .btn.processing{color:rgba(0, 0, 0, 0.2)}div.dt-buttons .btn.processing:after{position:absolute;top:50%;left:50%;width:16px;height:16px;margin:-8px 0 0 -8px;box-sizing:border-box;display:block;content:" ";border:2px solid rgb(40, 40, 40);border-radius:50%;border-left-color:transparent;border-right-color:transparent;animation:dtb-spinner 1500ms infinite linear;-o-animation:dtb-spinner 1500ms infinite linear;-ms-animation:dtb-spinner 1500ms infinite linear;-webkit-animation:dtb-spinner 1500ms infinite linear;-moz-animation:dtb-spinner 1500ms infinite linear}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999}@media screen and (max-width: 767px){div.dt-buttons{float:none;width:100%;text-align:center;margin-bottom:.5em}div.dt-buttons a.btn{float:none}}:root[data-bs-theme=dark] div.dropdown-menu.dt-button-collection.fixed{background-color:rgb(33, 37, 41);border:1px solid rgba(255, 255, 255, 0.15);border-radius:8px} diff --git a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.bootstrap5.min.js b/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.bootstrap5.min.js deleted file mode 100644 index e544ed9543..0000000000 --- a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.bootstrap5.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! Bootstrap integration for DataTables' Buttons - * © SpryMedia Ltd - datatables.net/license - */ -!function(o){var e,a;"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs5","datatables.net-buttons"],function(t){return o(t,window,document)}):"object"==typeof exports?(e=require("jquery"),a=function(t,n){n.fn.dataTable||require("datatables.net-bs5")(t,n),n.fn.dataTable.Buttons||require("datatables.net-buttons")(t,n)},"undefined"==typeof window?module.exports=function(t,n){return t=t||window,n=n||e(t),a(t,n),o(n,0,t.document)}:(a(window,e),module.exports=o(e,window,window.document))):o(jQuery,window,document)}(function(o,t,n,e){"use strict";var a=o.fn.dataTable;return o.extend(!0,a.Buttons.defaults,{dom:{container:{className:"dt-buttons btn-group flex-wrap"},button:{className:"btn btn-secondary",active:"active"},collection:{action:{dropHtml:""},container:{tag:"div",className:"dropdown-menu dt-button-collection"},closeButton:!1,button:{tag:"a",className:"dt-button dropdown-item",active:"dt-button-active",disabled:"disabled",spacer:{className:"dropdown-divider",tag:"hr"}}},split:{action:{tag:"a",className:"btn btn-secondary dt-button-split-drop-button",closeButton:!1},dropdown:{tag:"button",dropHtml:"",className:"btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split",closeButton:!1,align:"split-left",splitAlignClass:"dt-button-split-left"},wrapper:{tag:"div",className:"dt-button-split btn-group",closeButton:!1}}},buttonCreated:function(t,n){return t.buttons?o('
').append(n):n}}),a.ext.buttons.collection.className+=" dropdown-toggle",a.ext.buttons.collection.rightAlignClassName="dropdown-menu-right",a}); \ No newline at end of file diff --git a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.colVis.min.js b/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.colVis.min.js deleted file mode 100644 index ba1fecb75c..0000000000 --- a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.colVis.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Column visibility buttons for Buttons and DataTables. - * © SpryMedia Ltd - datatables.net/license - */ -!function(t){var o,i;"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(n){return t(n,window,document)}):"object"==typeof exports?(o=require("jquery"),i=function(n,e){e.fn.dataTable||require("datatables.net")(n,e),e.fn.dataTable.Buttons||require("datatables.net-buttons")(n,e)},"undefined"==typeof window?module.exports=function(n,e){return n=n||window,e=e||o(n),i(n,e),t(e,0,n.document)}:(i(window,o),module.exports=t(o,window,window.document))):t(jQuery,window,document)}(function(n,e,t,l){"use strict";var o=n.fn.dataTable;return n.extend(o.ext.buttons,{colvis:function(o,i){var l=null,n={extend:"collection",init:function(n,e){l=e},text:function(n){return n.i18n("buttons.colvis","Column visibility")},className:"buttons-colvis",closeButton:!1,buttons:[{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}]};return o.on("column-reorder.dt"+i.namespace,function(n,e,t){o.button(null,o.button(null,l).node()).collectionRebuild([{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}])}),n},columnsToggle:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnToggle",columns:n,columnText:e.columnText}}).toArray()},columnToggle:function(n,e){return{extend:"columnVisibility",columns:e.columns,columnText:e.columnText}},columnsVisibility:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnVisibility",columns:n,visibility:e.visibility,columnText:e.columnText}}).toArray()},columnVisibility:{columns:l,text:function(n,e,t){return t._columnText(n,t)},className:"buttons-columnVisibility",action:function(n,e,t,o){var e=e.columns(o.columns),i=e.visible();e.visible(o.visibility!==l?o.visibility:!(i.length&&i[0]))},init:function(o,n,i){var l=this;n.attr("data-cv-idx",i.columns),o.on("column-visibility.dt"+i.namespace,function(n,e){e.bDestroying||e.nTable!=o.settings()[0].nTable||l.active(o.column(i.columns).visible())}).on("column-reorder.dt"+i.namespace,function(n,e,t){i.destroying||1===o.columns(i.columns).count()&&(l.text(i._columnText(o,i)),l.active(o.column(i.columns).visible()))}),this.active(o.column(i.columns).visible())},destroy:function(n,e,t){n.off("column-visibility.dt"+t.namespace).off("column-reorder.dt"+t.namespace)},_columnText:function(n,e){var t=n.column(e.columns).index(),o=n.settings()[0].aoColumns[t].sTitle;return o=(o=o||n.column(t).header().innerHTML).replace(/\n/g," ").replace(//gi," ").replace(//g,"").replace(//g,"").replace(/<.*?>/g,"").replace(/^\s+|\s+$/g,""),e.columnText?e.columnText(n,t,o):o}},colvisRestore:{className:"buttons-colvisRestore",text:function(n){return n.i18n("buttons.colvisRestore","Restore visibility")},init:function(e,n,t){t._visOriginal=e.columns().indexes().map(function(n){return e.column(n).visible()}).toArray()},action:function(n,e,t,o){e.columns().every(function(n){n=e.colReorder&&e.colReorder.transpose?e.colReorder.transpose(n,"toOriginal"):n;this.visible(o._visOriginal[n])})}},colvisGroup:{className:"buttons-colvisGroup",action:function(n,e,t,o){e.columns(o.show).visible(!0,!1),e.columns(o.hide).visible(!1,!1),e.columns.adjust()},show:[],hide:[]}}),o}); \ No newline at end of file diff --git a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.html5.min.js b/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.html5.min.js deleted file mode 100644 index 9033bbf961..0000000000 --- a/Web/assets/vendor/datatables-buttons/2.4.2/js/buttons.html5.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * HTML5 export buttons for Buttons and DataTables. - * © SpryMedia Ltd - datatables.net/license - * - * FileSaver.js (1.3.3) - MIT license - * Copyright © 2016 Eli Grey - http://eligrey.com - */ -!function(n){var r,a;"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(t){return n(t,window,document)}):"object"==typeof exports?(r=require("jquery"),a=function(t,e){e.fn.dataTable||require("datatables.net")(t,e),e.fn.dataTable.Buttons||require("datatables.net-buttons")(t,e)},"undefined"==typeof window?module.exports=function(t,e,o,l){return t=t||window,e=e||r(t),a(t,e),n(e,t,t.document)}:(a(window,r),module.exports=n(r,window,window.document))):n(jQuery,window,document)}(function(C,T,y,t,e,k){"use strict";var o,l,n=C.fn.dataTable;function S(){return o||T.JSZip}function s(){return l||T.pdfMake}n.Buttons.pdfMake=function(t){if(!t)return s();l=t},n.Buttons.jszip=function(t){if(!t)return S();o=t};function N(t){var e="Sheet1";return e=t.sheetName?t.sheetName.replace(/[\[\]\*\/\\\?\:]/g,""):e}function u(t,e){for(var o=c(e),l=t.buttons.exportData(e.exportOptions),n=e.fieldBoundary,r=e.fieldSeparator,a=new RegExp(n,"g"),d=e.escapeChar!==k?e.escapeChar:"\\",p=function(t){for(var e="",o=0,l=t.length;o',"xl/_rels/workbook.xml.rels":'',"[Content_Types].xml":'',"xl/workbook.xml":'',"xl/worksheets/sheet1.xml":'',"xl/styles.xml":''},R=[{match:/^\-?\d+\.\d%$/,style:60,fmt:function(t){return t/100}},{match:/^\-?\d+\.?\d*%$/,style:56,fmt:function(t){return t/100}},{match:/^\-?\$[\d,]+.?\d*$/,style:57},{match:/^\-?£[\d,]+.?\d*$/,style:58},{match:/^\-?€[\d,]+.?\d*$/,style:59},{match:/^\-?\d+$/,style:65},{match:/^\-?\d+\.\d{2}$/,style:66},{match:/^\([\d,]+\)$/,style:61,fmt:function(t){return-1*t.replace(/[\(\)]/g,"")}},{match:/^\([\d,]+\.\d{2}\)$/,style:62,fmt:function(t){return-1*t.replace(/[\(\)]/g,"")}},{match:/^\-?[\d,]+$/,style:63},{match:/^\-?[\d,]+\.\d{2}$/,style:64},{match:/^[\d]{4}\-[01][\d]\-[0123][\d]$/,style:67,fmt:function(t){return Math.round(25569+Date.parse(t)/864e5)}}];return n.ext.buttons.copyHtml5={className:"buttons-copy buttons-html5",text:function(t){return t.i18n("buttons.copy","Copy")},action:function(t,e,o,l){this.processing(!0);var n=this,r=u(e,l),a=e.buttons.exportInfo(l),d=c(l),p=r.str,i=C("
").css({height:1,width:1,overflow:"hidden",position:"fixed",top:0,left:0}),d=(a.title&&(p=a.title+d+d+p),a.messageTop&&(p=a.messageTop+d+d+p),a.messageBottom&&(p=p+d+d+a.messageBottom),l.customize&&(p=l.customize(p,l,e)),C("