From f50d118b614eeb180db021c239cf73d6a901e612 Mon Sep 17 00:00:00 2001 From: Fritz Michael Gschwantner Date: Mon, 21 Sep 2020 13:39:16 +0200 Subject: [PATCH 1/2] Consider symlink when saving localconfig.php (see #2209) Description ----------- We are using [Deployer](https://deployer.org/) to deploy our Contao 4 instances. In the config, we have added ``` system/config/localconfig.php ``` to the `shared_files` so that any changes to the system config in the back end do not get lost after the next deployment. However, this does not actually work as Contao simply overwrites the symlink with a regular `localconfig.php` file when saving. This PR changes that: it evaluates the `realpath` of an existing `localconfig.php` and uses that path as the new destination. Commits ------- 84c34b8b consider symlinks when saving localconfig.php 3d6582b6 Merge branch '4.4' into consider-symlinks-for-localconfig 5a895ca5 remove unnecessary file_exists call `realpath` already checks for the existence of the file and returns `FALSE` in that case. 5489f4c5 fix typo d828189b add PR reference 99155c62 Remove PathUtil 14a67e5f Re-add PathUtil --- .../contao/library/Contao/Config.php | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Resources/contao/library/Contao/Config.php b/src/Resources/contao/library/Contao/Config.php index b726aa4331..28aadd0ed0 100644 --- a/src/Resources/contao/library/Contao/Config.php +++ b/src/Resources/contao/library/Contao/Config.php @@ -10,6 +10,9 @@ namespace Contao; +use Symfony\Component\Filesystem\Filesystem; +use Webmozart\PathUtil\Path; + /** * Loads and writes the local configuration file * @@ -244,37 +247,47 @@ public function save() $strFile .= "\n" . $this->strBottom . "\n"; } - $strTemp = md5(uniqid(mt_rand(), true)); + $strTemp = Path::join(TL_ROOT, 'system/tmp', md5(uniqid(mt_rand(), true))); // Write to a temp file first - $objFile = fopen(TL_ROOT . '/system/tmp/' . $strTemp, 'w'); + $objFile = fopen($strTemp, 'w'); fwrite($objFile, $strFile); fclose($objFile); // Make sure the file has been written (see #4483) - if (!filesize(TL_ROOT . '/system/tmp/' . $strTemp)) + if (!filesize($strTemp)) { - \System::log('The local configuration file could not be written. Have your reached your quota limit?', __METHOD__, TL_ERROR); + \System::log('The local configuration file could not be written. Have you reached your quota limit?', __METHOD__, TL_ERROR); return; } + $fs = new Filesystem(); + // Adjust the file permissions (see #8178) - $this->Files->chmod('system/tmp/' . $strTemp, \Config::get('defaultFileChmod')); + $fs->chmod($strTemp, self::get('defaultFileChmod')); + + $strDestination = Path::join(TL_ROOT, 'system/config/localconfig.php'); + + // Get the realpath in case it is a symlink (see #2209) + if ($realpath = realpath($strDestination)) + { + $strDestination = $realpath; + } // Then move the file to its final destination - $this->Files->rename('system/tmp/' . $strTemp, 'system/config/localconfig.php'); + $fs->rename($strTemp, $strDestination, true); // Reset the Zend OPcache if (\function_exists('opcache_invalidate')) { - opcache_invalidate(TL_ROOT . '/system/config/localconfig.php', true); + opcache_invalidate($strDestination, true); } // Recompile the APC file (thanks to Trenker) if (\function_exists('apc_compile_file') && !ini_get('apc.stat')) { - apc_compile_file(TL_ROOT . '/system/config/localconfig.php'); + apc_compile_file($strDestination); } $this->blnIsModified = false; From 37c744471cff84803f6d9c6dcbf02bc0eedb9928 Mon Sep 17 00:00:00 2001 From: Fritz Michael Gschwantner Date: Mon, 21 Sep 2020 16:12:39 +0200 Subject: [PATCH 2/2] Fix picker deselecting filtered elements (see #2296) Description ----------- | Q | A | -----------------| --- | Fixed issues | Fixes #1816 | Docs PR or issue | - This PR fixes the picker deselecting elements that are not visible any more due to filtering. It injects the previously selected elements for the `Backend.openModalSelector` JavaScript, which can then add or remove elements. Commits ------- 7bfaf70f fix picker deselecting filtered elements 4e054710 change issue reference 88d06b60 change issue reference 2329bfe3 declare pickerValue var fe1beb1c consolidate if b5f1c652 make leo happy a36dfa45 Run the Gulp task --- src/Resources/contao/drivers/DC_Folder.php | 10 +++++++- src/Resources/contao/drivers/DC_Table.php | 30 +++++++++++++++++++--- src/Resources/public/core.js | 19 +++++++++++--- src/Resources/public/core.min.js | 2 +- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/Resources/contao/drivers/DC_Folder.php b/src/Resources/contao/drivers/DC_Folder.php index 48c1675a72..10211c315e 100644 --- a/src/Resources/contao/drivers/DC_Folder.php +++ b/src/Resources/contao/drivers/DC_Folder.php @@ -417,6 +417,14 @@ public function showAll() \Message::addInfo($GLOBALS['TL_LANG']['MSC']['searchExclude']); } + // Pass previously selected values to picker (#1816) + $prevPickerValue = ''; + + if ($this->strPickerFieldType) + { + $prevPickerValue = ' data-picker-value="' . htmlspecialchars(json_encode(array_map('strval', $this->arrPickerValue))) . '"'; + } + // Build the tree $return = $this->panel() . \Message::generate() . '
' . ((\Input::get('act') == 'select') ? ' @@ -432,7 +440,7 @@ public function showAll()

' . $GLOBALS['TL_LANG']['MSC']['selectNewPosition'] . '

' : '') . ' -
' . (isset($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['breadcrumb']) ? $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['breadcrumb'] : '') . ((\Input::get('act') == 'select' || $this->strPickerFieldType == 'checkbox') ? ' +
' . (isset($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['breadcrumb']) ? $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['breadcrumb'] : '') . ((\Input::get('act') == 'select' || $this->strPickerFieldType == 'checkbox') ? '
' : '') . ' diff --git a/src/Resources/contao/drivers/DC_Table.php b/src/Resources/contao/drivers/DC_Table.php index 533e1dcdc3..303ff9480a 100644 --- a/src/Resources/contao/drivers/DC_Table.php +++ b/src/Resources/contao/drivers/DC_Table.php @@ -3597,6 +3597,14 @@ protected function treeView()

' . $GLOBALS['TL_LANG']['MSC']['noResult'] . '

'; } + // Pass previously selected values to picker (#1816) + $prevPickerValue = ''; + + if ($this->strPickerFieldType) + { + $prevPickerValue = ' data-picker-value="' . htmlspecialchars(json_encode(array_map('strval', $this->arrPickerValue))) . '"'; + } + $return .= ((\Input::get('act') == 'select') ? '
@@ -3605,7 +3613,7 @@ protected function treeView()

' . $GLOBALS['TL_LANG']['MSC']['selectNewPosition'] . '

' : '') . ' -
' . $breadcrumb . ((\Input::get('act') == 'select' || ($this->strPickerFieldType == 'checkbox')) ? ' +
' . $breadcrumb . ((\Input::get('act') == 'select' || ($this->strPickerFieldType == 'checkbox')) ? '
' : '') . ' @@ -4128,6 +4136,14 @@ protected function parentView() return $return; } + // Pass previously selected values to picker (#1816) + $prevPickerValue = ''; + + if ($this->strPickerFieldType) + { + $prevPickerValue = ' data-picker-value="' . htmlspecialchars(json_encode(array_map('strval', $this->arrPickerValue))) . '"'; + } + $return .= ((\Input::get('act') == 'select') ? ' @@ -4137,7 +4153,7 @@ protected function parentView()

' . $GLOBALS['TL_LANG']['MSC']['selectNewPosition'] . '

' : '') . ' -
+
'; // List all records of the child table @@ -4757,12 +4773,20 @@ protected function listView() { $result = $objRow->fetchAllAssoc(); + // Pass previously selected values to picker (#1816) + $prevPickerValue = ''; + + if ($this->strPickerFieldType) + { + $prevPickerValue = ' data-picker-value="' . htmlspecialchars(json_encode(array_map('strval', $this->arrPickerValue))) . '"'; + } + $return .= ((\Input::get('act') == 'select') ? '
' : '') . ' -
' . ((\Input::get('act') == 'select' || $this->strPickerFieldType == 'checkbox') ? ' +
' . ((\Input::get('act') == 'select' || $this->strPickerFieldType == 'checkbox') ? '
' : '') . ' diff --git a/src/Resources/public/core.js b/src/Resources/public/core.js index 3fb07f5277..6b60c30ea4 100644 --- a/src/Resources/public/core.js +++ b/src/Resources/public/core.js @@ -920,16 +920,29 @@ var Backend = return; } var frm = window.frames['simple-modal-iframe'], - val = [], ul, inp, field, act, it, i; + val = [], ul, inp, field, act, it, i, pickerValue, sIndex; if (frm === undefined) { alert('Could not find the SimpleModal frame'); return; } ul = frm.document.getElementById(opt.id); + // Load the previous values (#1816) + if (pickerValue = ul.get('data-picker-value')) { + val = JSON.parse(pickerValue); + } inp = ul.getElementsByTagName('input'); for (i=0; in)&&(t.width=Math.min(n,900)),new SimpleModal({width:t.width,hideFooter:!0,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}}).show({title:t.title,contents:''})},openModalIframe:function(e){var t=e||{},n=(window.getSize().x-20).toInt(),a=(window.getSize().y-137).toInt();(!t.width||t.width>n)&&(t.width=Math.min(n,900)),(!t.height||t.height>a)&&(t.height=a),new SimpleModal({width:t.width,hideFooter:!0,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}}).show({title:t.title,contents:''})},openModalSelector:function(e){var c=e||{},t=(window.getSize().x-20).toInt(),n=(window.getSize().y-192).toInt();c.id||(c.id="tl_select"),(!c.width||c.width>t)&&(c.width=Math.min(t,900)),(!c.height||c.height>n)&&(c.height=n);var a=new SimpleModal({width:c.width,btn_ok:Contao.lang.close,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}});a.addButton(Contao.lang.close,"btn",function(){this.buttons[0].hasClass("btn-disabled")||this.hide()}),a.addButton(Contao.lang.apply,"btn primary",function(){if(!this.buttons[1].hasClass("btn-disabled")){var e,t,n,a,o,i,l=window.frames["simple-modal-iframe"],s=[];if(void 0!==l){for(t=(e=l.document.getElementById(c.id)).getElementsByTagName("input"),i=0;i',model:"modal"})},openModalBrowser:function(n,e,t,a,o){Backend.openModalSelector({id:"tl_listing",title:a.document.getElement("div.mce-title").get("text"),url:Contao.routes.backend_picker+"?context="+("file"==t?"link":"file")+"&extras[fieldType]=radio&extras[filesOnly]=true&extras[source]="+o+"&value="+e+"&popup=1",callback:function(e,t){a.document.getElementById(n).value=t.join(",")}})},getScrollOffset:function(){document.cookie="BE_PAGE_OFFSET="+window.getScroll().y+"; path="+(Contao.path||"/")},autoSubmit:function(e){Backend.getScrollOffset();var t=new Element("input",{type:"hidden",name:"SUBMIT_TYPE",value:"auto"}),n=$(e)||e;t.inject(n,"bottom"),n.submit()},vScrollTo:function(e){window.addEvent("load",function(){window.scrollTo(null,parseInt(e))})},limitPreviewHeight:function(){var i=0;$$("div.limit_height").each(function(e){var t,n,a,o;0===i&&(i=e.className.replace(/[^0-9]*/,"").toInt()),i&&(t=new Element("div",{class:"limit_toggler"}),n=new Element("button",{type:"button",html:"...",class:"unselectable","data-state":0}).inject(t),a=e.getCoordinates(),e.setStyle("height",i),a.height<=i||(n.addEvent("click",function(){o=t.getPrevious("div").getStyle("height").toInt(),t.getPrevious("div").setStyle("height",in)&&(t.width=Math.min(n,900)),new SimpleModal({width:t.width,hideFooter:!0,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}}).show({title:t.title,contents:''})},openModalIframe:function(e){var t=e||{},n=(window.getSize().x-20).toInt(),a=(window.getSize().y-137).toInt();(!t.width||t.width>n)&&(t.width=Math.min(n,900)),(!t.height||t.height>a)&&(t.height=a),new SimpleModal({width:t.width,hideFooter:!0,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}}).show({title:t.title,contents:''})},openModalSelector:function(e){var d=e||{},t=(window.getSize().x-20).toInt(),n=(window.getSize().y-192).toInt();d.id||(d.id="tl_select"),(!d.width||d.width>t)&&(d.width=Math.min(t,900)),(!d.height||d.height>n)&&(d.height=n);var a=new SimpleModal({width:d.width,btn_ok:Contao.lang.close,draggable:!1,overlayOpacity:.7,onShow:function(){document.body.setStyle("overflow","hidden")},onHide:function(){document.body.setStyle("overflow","auto")}});a.addButton(Contao.lang.close,"btn",function(){this.buttons[0].hasClass("btn-disabled")||this.hide()}),a.addButton(Contao.lang.apply,"btn primary",function(){if(!this.buttons[1].hasClass("btn-disabled")){var e,t,n,a,o,i,l,s,c=window.frames["simple-modal-iframe"],r=[];if(void 0!==c){for((l=(e=c.document.getElementById(d.id)).get("data-picker-value"))&&(r=JSON.parse(l)),t=e.getElementsByTagName("input"),i=0;i',model:"modal"})},openModalBrowser:function(n,e,t,a,o){Backend.openModalSelector({id:"tl_listing",title:a.document.getElement("div.mce-title").get("text"),url:Contao.routes.backend_picker+"?context="+("file"==t?"link":"file")+"&extras[fieldType]=radio&extras[filesOnly]=true&extras[source]="+o+"&value="+e+"&popup=1",callback:function(e,t){a.document.getElementById(n).value=t.join(",")}})},getScrollOffset:function(){document.cookie="BE_PAGE_OFFSET="+window.getScroll().y+"; path="+(Contao.path||"/")},autoSubmit:function(e){Backend.getScrollOffset();var t=new Element("input",{type:"hidden",name:"SUBMIT_TYPE",value:"auto"}),n=$(e)||e;t.inject(n,"bottom"),n.submit()},vScrollTo:function(e){window.addEvent("load",function(){window.scrollTo(null,parseInt(e))})},limitPreviewHeight:function(){var i=0;$$("div.limit_height").each(function(e){var t,n,a,o;0===i&&(i=e.className.replace(/[^0-9]*/,"").toInt()),i&&(t=new Element("div",{class:"limit_toggler"}),n=new Element("button",{type:"button",html:"...",class:"unselectable","data-state":0}).inject(t),a=e.getCoordinates(),e.setStyle("height",i),a.height<=i||(n.addEvent("click",function(){o=t.getPrevious("div").getStyle("height").toInt(),t.getPrevious("div").setStyle("height",i