From fec1c6593bff2b0eed1c5328d064bab154077d6f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 19 Nov 2021 10:48:56 +0100 Subject: [PATCH 01/19] fix PHP 8.0 TypeError: array_search(): Argument #2 ($haystack) must be of type array, null given --- addressbook/inc/class.addressbook_zpush.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addressbook/inc/class.addressbook_zpush.inc.php b/addressbook/inc/class.addressbook_zpush.inc.php index 307133cb91..879e074d1e 100644 --- a/addressbook/inc/class.addressbook_zpush.inc.php +++ b/addressbook/inc/class.addressbook_zpush.inc.php @@ -557,7 +557,7 @@ class addressbook_zpush implements activesync_plugin_write, activesync_plugin_se case 'cat_id': // for existing entries in all-in-one addressbook, remove addressbook name as category - if ($contact && $GLOBALS['egw_info']['user']['preferences']['activesync']['addressbook-all-in-one'] && + if ($contact && $GLOBALS['egw_info']['user']['preferences']['activesync']['addressbook-all-in-one'] && is_array($message->$key) && ($k=array_search($this->get_addressbooks($contact['owner'].($contact['private']?'p':''), false, true),$message->$key))) { unset($message->categories[$k]); From 47b3f5c239894c8d77e34cc77340bbebe7a03da0 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 19 Nov 2021 13:00:51 +0100 Subject: [PATCH 02/19] fix PHP 8.0 TypeError: array_keys(): Argument #1 ($array) must be of type array, null given --- api/src/Contacts/Sql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Contacts/Sql.php b/api/src/Contacts/Sql.php index 3dfc932ac2..50c7edd81d 100644 --- a/api/src/Contacts/Sql.php +++ b/api/src/Contacts/Sql.php @@ -513,7 +513,7 @@ class Sql extends Api\Storage $shared_sql = $this->table_name.'.contact_id IN (SELECT contact_id FROM '.self::SHARED_TABLE.' WHERE '. // $filter[tid] === null is used by sync-collection report, in which case we need to return deleted shares, to remove them from devices (array_key_exists('tid', $filter) && !isset($filter['tid']) ? '' : 'shared_deleted IS NULL AND '). - $this->db->expression(self::SHARED_TABLE, ['shared_with' => $filter['owner'] ?? array_keys($this->grants)]).')'; + $this->db->expression(self::SHARED_TABLE, ['shared_with' => $filter['owner'] ?? array_keys($this->grants ?? [0])]).')'; } // add filter for read ACL in sql, if user is NOT the owner of the addressbook From 5aed6918bd4a40b267135f6522ac67cf28570e8f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 20 Nov 2021 08:36:35 +0100 Subject: [PATCH 03/19] fix PHP 8.0 TypeError: implode(): Argument #1 ($pieces) must be of type array, string given --- infolog/inc/class.infolog_bo.inc.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index 15beb6bfbb..c95eb17f0d 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -1396,6 +1396,7 @@ class infolog_bo */ function import_mail($_addresses,$_subject,$_message,$_attachments,$_date) { + $names = $emails = []; foreach($_addresses as $address) { $names[] = $address['name']; @@ -1407,7 +1408,7 @@ class infolog_bo $info = array( 'info_id' => 0, 'info_type' => $type, - 'info_from' => implode(', ',$names) . implode(', ', $emails), + 'info_from' => implode(', ', $names) . implode(', ', $emails), 'info_subject' => $_subject, 'info_des' => $_message, 'info_startdate' => Api\DateTime::server2user($_date), From 8020301a84c78847e2b5152d3db4e9ce331090a6 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 20 Nov 2021 08:40:35 +0100 Subject: [PATCH 04/19] fix PHP 8.0 TypeError: Unsupported operand types: int | array --- calendar/inc/class.calendar_boupdate.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 5b0d503005..0116a7a611 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -1746,7 +1746,7 @@ class calendar_boupdate extends calendar_bo static $memberships=null; if (is_null($memberships)) { - $memberships = $GLOBALS['egw']->accounts->memberships($user,true); + $memberships = $GLOBALS['egw']->accounts->memberships($user,true) ?: []; $memberships[] = $user; } foreach($cat_rights as $uid => $value) From dd74e87a0c56978e82189d370a2e366bbc1260cb Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 22 Nov 2021 08:36:55 +0100 Subject: [PATCH 05/19] fix PHP 8.0 TypeError: implode(): Argument #1 ($pieces) must be of type array, string given --- importexport/inc/class.importexport_definitions_ui.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/importexport/inc/class.importexport_definitions_ui.inc.php b/importexport/inc/class.importexport_definitions_ui.inc.php index 868496a43b..24bcce77a3 100644 --- a/importexport/inc/class.importexport_definitions_ui.inc.php +++ b/importexport/inc/class.importexport_definitions_ui.inc.php @@ -818,9 +818,9 @@ class importexport_definitions_ui if($this->can_edit($content)) { $content['owner'] = $content['just_me'] || !$GLOBALS['egw']->acl->check('share_definitions', Acl::READ,'importexport') ? - ($content['owner'] ? $content['owner'] : $GLOBALS['egw_info']['user']['account_id']) : + ($content['owner'] ?: $GLOBALS['egw_info']['user']['account_id']) : null; - $content['allowed_users'] = $content['just_me'] ? '' : ($content['all_users'] ? 'all' : implode(',',$content['allowed_users'])); + $content['allowed_users'] = $content['just_me'] ? '' : ($content['all_users'] ? 'all' : implode(',', (array)$content['allowed_users'])); unset($content['just_me']); } From 76e4a2acd33b93987ee96712ff5cdd7edac6b7ac Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 22 Nov 2021 08:41:01 +0100 Subject: [PATCH 06/19] fix PHP 8.0 TypeError: Unsupported operand types: string + string --- importexport/inc/class.importexport_schedule_ui.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/importexport/inc/class.importexport_schedule_ui.inc.php b/importexport/inc/class.importexport_schedule_ui.inc.php index f89f00a844..23d190f947 100644 --- a/importexport/inc/class.importexport_schedule_ui.inc.php +++ b/importexport/inc/class.importexport_schedule_ui.inc.php @@ -551,7 +551,7 @@ class importexport_schedule_ui foreach($po->get_warnings() as $record => $msg) { $data['warnings'][$target][] = "#$record: $msg"; - $buffer += "$record\t$msg\n"; + $buffer .= "$record\t$msg\n"; } error_log($buffer); } @@ -561,7 +561,7 @@ class importexport_schedule_ui foreach($po->get_errors() as $record => $error) { $data['errors'][$target][] = "#$record: $error"; - $buffer += "$record\t$error\n"; + $buffer .= "$record\t$error\n"; } error_log($buffer); } From 8239e5ad91153c5d3c8ff0a0a5eea2a304810abc Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 22 Nov 2021 11:07:25 +0100 Subject: [PATCH 07/19] fix PHP 8.0 TypeError: Cannot access offset of type string on string using renames method calendar_ical::iCalSearch() instead of ::search() which required unchanged signature of calendar_bo::search() --- mail/inc/class.mail_compose.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail/inc/class.mail_compose.inc.php b/mail/inc/class.mail_compose.inc.php index 052d9f5bdf..04a048d261 100644 --- a/mail/inc/class.mail_compose.inc.php +++ b/mail/inc/class.mail_compose.inc.php @@ -2077,7 +2077,7 @@ class mail_compose { //error_log(__METHOD__."about to call calendar_ical"); $calendar_ical = new calendar_ical(); - $eventid = $calendar_ical->search($attachment['attachment'],-1); + $eventid = $calendar_ical->iCalSearch($attachment['attachment'],-1); //error_log(__METHOD__.array2string($eventid)); if (!$eventid) $eventid = -1; $event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true); From 87c17d5e750e946cdaa32d705a09cff2199c8760 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 22 Nov 2021 13:09:00 +0100 Subject: [PATCH 08/19] * Admin/Filemanager: correctly encode user "WORKGROUP\$user" for SMB mounts and do NOT require mountpoints to exist same as for filemanager/cli.php --- filemanager/inc/class.filemanager_admin.inc.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/filemanager/inc/class.filemanager_admin.inc.php b/filemanager/inc/class.filemanager_admin.inc.php index a554c67653..8d4269fa5f 100644 --- a/filemanager/inc/class.filemanager_admin.inc.php +++ b/filemanager/inc/class.filemanager_admin.inc.php @@ -160,7 +160,7 @@ class filemanager_admin extends filemanager_ui { throw new Api\Exception\WrongUserinput(lang('SMB, WebDAVs and VFS require a username!')); } - $url .= $content['mounts']['url']['user'] === '$user' ? '$user' : urlencode(trim($content['mounts']['url']['user'])); + $url .= str_replace(urlencode('$user'), '$user', urlencode(trim($content['mounts']['url']['user']))); if (!empty($content['mounts']['url']['pass'])) { $url .= ':' . ($content['mounts']['url']['pass'] === '$pass' ? '$pass' : urlencode(trim($content['mounts']['url']['pass']))); @@ -174,7 +174,7 @@ class filemanager_admin extends filemanager_ui { throw new Api\Exception\WrongUserinput(lang('Versioning requires EGroupware EPL')); } - elseif (!Vfs::file_exists(Vfs::decodePath($path)) || Vfs::file_exists($path) && !Vfs::is_dir($path)) + elseif (Vfs::file_exists($path) && !Vfs::is_dir($path)) { throw new Api\Exception\WrongUserinput(lang('Path %1 not found or not a directory!', $path)); } @@ -186,7 +186,8 @@ class filemanager_admin extends filemanager_ui else { $msg = Vfs::mount($url, $path, true) ? - lang('Successful mounted %1 on %2.', $url, $path) : lang('Error mounting %1 on %2!', $url, $path); + lang('Successful mounted %1 on %2.', str_replace('%5C', '\\', $url), $path) : + lang('Error mounting %1 on %2!', str_replace('%5C', '\\', $url), $path); } } if ($content['allow_delete_versions'] != $GLOBALS['egw_info']['server']['allow_delete_versions']) @@ -297,7 +298,8 @@ class filemanager_admin extends filemanager_ui { $content['mounts'][$n++] = array( 'path' => $path, - 'url' => preg_replace('#://([^:@/]+):((?!\$pass)[^@/]+)@#', '://$1:****@', $url), + 'url' => preg_replace('#://([^:@/]+):((?!\$pass)[^@/]+)@#', '://$1:****@', + str_replace('%5c', '\\', $url)), ); $readonlys["disable[$path]"] = !$this->versioning || !Vfs::$is_root || Vfs::parse_url($url,PHP_URL_SCHEME) != $this->versioning; From 241d3181c90800ccf1de6cccc7ae69db8480634d Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 22 Nov 2021 09:11:13 -0700 Subject: [PATCH 09/19] Infolog: Fix save conflict was still overwriting --- infolog/inc/class.infolog_bo.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index c95eb17f0d..8b575bc79a 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -1006,7 +1006,7 @@ class infolog_bo $values['link_to']['to_id'] = $info_id; } } - if($values['info_id'] && $info_id !== false) + if($values['info_id'] && $info_id) { $this->write_check_links($to_write); if(!$values['info_link_id'] || $values['info_link_id'] != $to_write['info_link_id']) From cb65460b5ffc59e63f37e6cc669ca3f42a2ebb04 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 22 Nov 2021 17:53:37 +0100 Subject: [PATCH 10/19] * InfoLog: fix not working overwrite check (optimistic locking) plus incrementing etag --- infolog/inc/class.infolog_bo.inc.php | 9 +++++++-- infolog/inc/class.infolog_so.inc.php | 14 +++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index 8b575bc79a..e55a60e758 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -961,6 +961,10 @@ class infolog_bo $check_modified = $values['info_datemodified'] && !$xmlrpc ? $to_write['info_datemodified'] : false; $values['info_datemodified'] = $this->user_time_now; $to_write['info_datemodified'] = $this->now; + if ($check_modified && isset($values['info_etag'])) + { + ++$values['info_etag']; + } } if ($touch_modified || !$values['info_modifier']) { @@ -979,7 +983,7 @@ class infolog_bo if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs, !isset($old)))) { - if(!isset($values['info_type']) || $status_only || empty($values['caldav_url'])) + if(!isset($values['info_type']) || $status_only || empty($values['caldav_name'])) { $values = $this->read($info_id, true, 'server', $ignore_acl); } @@ -1006,12 +1010,13 @@ class infolog_bo $values['link_to']['to_id'] = $info_id; } } - if($values['info_id'] && $info_id) + if ($values['info_id'] && $info_id) { $this->write_check_links($to_write); if(!$values['info_link_id'] || $values['info_link_id'] != $to_write['info_link_id']) { // Just got a link ID, need to save it + unset($to_write['info_etag']); // we must not increment it again $this->so->write($to_write); $values['info_link_id'] = $to_write['info_link_id']; $values['info_contact'] = $to_write['info_contact']; diff --git a/infolog/inc/class.infolog_so.inc.php b/infolog/inc/class.infolog_so.inc.php index 95e0e96861..c1bd5bcd03 100644 --- a/infolog/inc/class.infolog_so.inc.php +++ b/infolog/inc/class.infolog_so.inc.php @@ -584,7 +584,19 @@ class infolog_so if (($this->data['info_id'] = $info_id) && !$force_insert) { $where = array('info_id' => $info_id); - if ($check_modified) $where['info_datemodified'] = $check_modified; + if ($check_modified) + { + $where['info_datemodified'] = $check_modified; + + // also check etag, if we got it + if (isset($values['info_etag'])) + { + $where['info_etag'] = $values['info_etag']; + } + unset($to_write['info_etag']); + // and increment it + $to_write[] = 'info_etag=info_etag+1'; + } if (!$this->db->update($this->info_table,$to_write,$where,__LINE__,__FILE__)) { //error_log("### soinfolog::write(".print_r($to_write,true).") where=".print_r($where,true)." returning false"); From 650268726807fe9d2b99712d0128eb298f38db34 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 22 Nov 2021 10:28:29 -0700 Subject: [PATCH 11/19] Fix infolog double PM change test --- infolog/tests/DoubleLinkPMTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/infolog/tests/DoubleLinkPMTest.php b/infolog/tests/DoubleLinkPMTest.php index c14517e90d..0fe9af73e3 100644 --- a/infolog/tests/DoubleLinkPMTest.php +++ b/infolog/tests/DoubleLinkPMTest.php @@ -173,7 +173,12 @@ class DoubleLinkPMTest extends \EGroupware\Api\EtemplateTest $this->makeProject('3'); $new_project = $this->pm_id[2]; + // Sleep for a bit to make the modified time different, or it will fail + sleep(1); + // Fake opening the edit dialog, important not to pass an array to accurately copy normal behaviour + // New BO to make sure we get a clean load, no caching + $this->ui->bo = $this->bo = new \infolog_bo(); $this->ui->edit($this->info_id); $content = self::$mocked_exec_result; From 4cc8eea7b6ea20ffd6ff94b8b37d1487e5755bf7 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 22 Nov 2021 11:56:42 -0700 Subject: [PATCH 12/19] Mail: Make sure pressing [del] key twice in a row does not delete the first row on the second press --- mail/js/app.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/mail/js/app.js b/mail/js/app.js index 53cf03230c..be0a97cacd 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -16,7 +16,8 @@ import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget"; import {et2_dialog} from "../../api/js/etemplate/et2_widget_dialog"; import {et2_button} from "../../api/js/etemplate/et2_widget_button"; import {egw_getObjectManager} from '../../api/js/egw_action/egw_action.js'; -import {egwIsMobile} from "../../api/js/egw_action/egw_action_common.js"; +import {egwIsMobile, egwSetBit} from "../../api/js/egw_action/egw_action_common.js"; +import {EGW_AO_FLAG_DEFAULT_FOCUS} from "../../api/js/egw_action/egw_action_constants.js"; import {egw_keycode_translation_function, egw_keycode_makeValid} from "../../api/js/egw_action/egw_keymanager.js"; /* required dependency, commented out because no module, but egw:uses is no longer parsed */ @@ -198,7 +199,7 @@ app.classes.mail = AppJS.extend( break; case 'mail.index': var self = this; - jQuery('iframe#mail-index_messageIFRAME').on('load', function() + jQuery('iframe#mail-index_messageIFRAME').on('load', function () { // decrypt preview body if mailvelope is available self.mailvelopeAvailable(self.mailvelopeDisplay); @@ -207,20 +208,24 @@ app.classes.mail = AppJS.extend( var nm = this.et2.getWidgetById(this.nm_index); this.mail_isMainWindow = true; + // Stop list from focussing next row on keypress + egw_getObjectManager('nm').flags = egwSetBit(egw_getObjectManager('nm').flags, EGW_AO_FLAG_DEFAULT_FOCUS, false); + // Set preview pane state this.mail_disablePreviewArea(!this.getPreviewPaneState()); //Get initial folder status - this.mail_refreshFolderStatus(undefined,undefined,false); + this.mail_refreshFolderStatus(undefined, undefined, false); // Bind to nextmatch refresh to update folder status - if(nm != null && (typeof jQuery._data(nm).events=='undefined'||typeof jQuery._data(nm).events.refresh == 'undefined')) + if (nm != null && (typeof jQuery._data(nm).events == 'undefined' || typeof jQuery._data(nm).events.refresh == 'undefined')) { var self = this; - jQuery(nm).on('refresh',function(_event, _widget, _row_id, _type) { + jQuery(nm).on('refresh', function (_event, _widget, _row_id, _type) + { if (!self.push_active[_widget.settings.foldertree.split("::")[0]]) { - self.mail_refreshFolderStatus.call(self,undefined,undefined,false); + self.mail_refreshFolderStatus.call(self, undefined, undefined, false); } }); } @@ -2189,11 +2194,11 @@ app.classes.mail = AppJS.extend( } // Tell server - egw.json('mail.mail_ui.ajax_deleteMessages',[_msg,(typeof _action == 'undefined'?'no':_action)]) + egw.json('mail.mail_ui.ajax_deleteMessages', [_msg, (typeof _action == 'undefined' ? 'no' : _action)]) .sendRequest(true); if (_msg['all']) this.egw.refresh(this.egw.lang("deleted %1 messages in %2",(_msg['all']?egw.lang('all'):_msg['msg'].length),(displayname?displayname:egw.lang('current folder'))),'mail');//,ids,'delete'); - this.egw.message(this.egw.lang("deleted %1 messages in %2",(_msg['all']?egw.lang('all'):_msg['msg'].length),(displayname?displayname:egw.lang('current Folder'))), 'success'); + this.egw.message(this.egw.lang("deleted %1 messages in %2", (_msg['all'] ? egw.lang('all') : _msg['msg'].length), (displayname ? displayname : egw.lang('current Folder'))), 'success'); }, /** @@ -2214,7 +2219,7 @@ app.classes.mail = AppJS.extend( { for (var i = 0; i < _msg['msg'].length; i++) { - this.egw.refresh(_msg['egw_message'], 'mail', _msg['msg'][i].replace(/mail::/,''), 'delete'); + this.egw.refresh(_msg['egw_message'], 'mail', _msg['msg'][i].replace(/mail::/, ''), 'delete'); } // Nextmatch automatically selects the next row and calls preview. From e2f06d0b9405e0b5f80740b48230fe64ed33acd6 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 23 Nov 2021 08:40:58 +0100 Subject: [PATCH 13/19] fix PHP 8.0 TypeError: count(): Argument #1 ($value) must be of type Countable|array, bool given --- importexport/inc/class.importexport_schedule_ui.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importexport/inc/class.importexport_schedule_ui.inc.php b/importexport/inc/class.importexport_schedule_ui.inc.php index 23d190f947..34acfb4cc0 100644 --- a/importexport/inc/class.importexport_schedule_ui.inc.php +++ b/importexport/inc/class.importexport_schedule_ui.inc.php @@ -317,7 +317,7 @@ class importexport_schedule_ui 'method' => 'HEAD', 'ignore_errors' => 1 ))); - $headers = get_headers($data['target'],1); + $headers = get_headers($data['target'],1) ?: []; // Reset... stream_context_set_default(array('http'=>array( From dd738845343e79184ed67f4cd3aa6146aa767a1e Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 23 Nov 2021 17:17:48 +0100 Subject: [PATCH 14/19] fix PHP 8.0 Error: Illegal string offset 'string' --- importexport/inc/class.importexport_definition.inc.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/importexport/inc/class.importexport_definition.inc.php b/importexport/inc/class.importexport_definition.inc.php index 8626819bf8..ad3399d3a5 100644 --- a/importexport/inc/class.importexport_definition.inc.php +++ b/importexport/inc/class.importexport_definition.inc.php @@ -193,10 +193,12 @@ class importexport_definition implements importexport_iface_egw_record { * * @param array $options */ - private function set_options(array $_plugin_options) { + private function set_options(array $_plugin_options) + { // Check conditions - foreach ( (Array)$_plugin_options['conditions'] as $key => $condition ) { - if(!$condition['string'] && array_key_exists($key, $_plugin_options['conditions'])) + foreach ((array)$_plugin_options['conditions'] as $key => $condition) + { + if (is_array($condition) && empty($condition['string']) && array_key_exists($key, $_plugin_options['conditions'])) { unset($_plugin_options['conditions'][$key]); } From ce76fa5d7f83635f78baf8aab2e7b94b08cd7f43 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 23 Nov 2021 17:23:49 +0100 Subject: [PATCH 15/19] fix PHP 8.0 TypeError: Unsupported operand types: string & int --- api/src/Mail/Account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Mail/Account.php b/api/src/Mail/Account.php index 6d962be7a2..e34f6dfa8c 100644 --- a/api/src/Mail/Account.php +++ b/api/src/Mail/Account.php @@ -281,7 +281,7 @@ class Account implements \ArrayAccess public static function ssl2secure($ssl) { $secure = false; - switch($ssl & ~self::SSL_VERIFY) + switch((int)$ssl & ~self::SSL_VERIFY) { case self::SSL_STARTTLS: $secure = 'tls'; // Horde uses 'tls' for STARTTLS, not ssl connection with tls version >= 1 and no sslv2/3 From 4bf375c970db263182002318ca1924bfec4f5e0d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 23 Nov 2021 17:44:46 +0100 Subject: [PATCH 16/19] fix PHP 8.0 TypeError: Illegal offset type --- api/src/Storage/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Storage/Base.php b/api/src/Storage/Base.php index 3fa3d70d27..3a6e0081bb 100644 --- a/api/src/Storage/Base.php +++ b/api/src/Storage/Base.php @@ -513,7 +513,7 @@ class Base $q = array(); foreach($col as $db_c => $c) { - if ($this->data[$col] == '') + if ($this->data[$c] == '') { $q = null; break; From 68a79dce6a0a4ca7c6989a272cb4787ec08df04d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 23 Nov 2021 18:07:49 +0100 Subject: [PATCH 17/19] fix PHP 8.0 TypeError: Unsupported operand types --- api/src/Storage/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Storage/Base.php b/api/src/Storage/Base.php index 3a6e0081bb..072d3d2de7 100644 --- a/api/src/Storage/Base.php +++ b/api/src/Storage/Base.php @@ -492,7 +492,7 @@ class Base $this->init($keys); $this->data2db(); - $query = false; + $query = []; foreach ($this->db_key_cols as $db_col => $col) { if ($this->data[$col] != '') From 86c9b97e47f2ed90a642628391db7cf5dc4fbf2a Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 23 Nov 2021 15:50:10 -0700 Subject: [PATCH 18/19] Calendar: Update merge to use changes introduced with 707c57e --- calendar/inc/class.calendar_merge.inc.php | 121 ++++++++++++++-- calendar/inc/class.calendar_ui.inc.php | 147 -------------------- calendar/inc/class.calendar_uiviews.inc.php | 27 ---- calendar/js/app.ts | 27 ++-- 4 files changed, 129 insertions(+), 193 deletions(-) diff --git a/calendar/inc/class.calendar_merge.inc.php b/calendar/inc/class.calendar_merge.inc.php index 51960aea56..978ef11eb0 100644 --- a/calendar/inc/class.calendar_merge.inc.php +++ b/calendar/inc/class.calendar_merge.inc.php @@ -129,6 +129,106 @@ class calendar_merge extends Api\Storage\Merge return parent::merge_string($content, $ids, $err, $mimetype, $fix, $charset); } + public static function merge_entries(array $ids = null, \EGroupware\Api\Storage\Merge &$document_merge = null, $pdf = null) + { + $document_merge = new calendar_merge(); + + if(is_null(($ids))) + { + $ids = json_decode($_REQUEST['id'], true); + } + + // Try to make time span into appropriate ranges to match + $template = $ids['view'] ?: ''; + if(stripos($_REQUEST['document'], 'month') !== false || stripos($_REQUEST['document'], lang('month')) !== false) + { + $template = 'month'; + } + if(stripos($_REQUEST['document'], 'week') !== false || stripos($_REQUEST['document'], lang('week')) !== false) + { + $template = 'week'; + } + + //error_log("Detected template $template"); + $date = $ids['date']; + $first = $ids['first']; + $last = $ids['last']; + + // Pull dates from session if they're not in the request + if(!array_key_exists('first', $ids)) + { + $ui = new calendar_ui(); + $date = $ui->date; + $first = $ui->first; + $last = $ui->last; + } + switch($template) + { + case 'month': + // Trim to _only_ the month, do not pad to week start / end + $time = new Api\DateTime($date); + $timespan = array(array( + 'start' => Api\DateTime::to($time->format('Y-m-01 00:00:00'), 'ts'), + 'end' => Api\DateTime::to($time->format('Y-m-t 23:59:59'), 'ts') + )); + break; + case 'week': + $timespan = array(); + $start = new Api\DateTime($first); + $end = new Api\DateTime($last); + $t = clone $start; + $t->modify('+1 week')->modify('-1 second'); + if($t < $end) + { + do + { + $timespan[] = array( + 'start' => $start->format('ts'), + 'end' => $t->format('ts') + ); + $start->modify('+1 week'); + $t->modify('+1 week'); + } + while($start < $end); + break; + } + // Fall through + default: + $timespan = array(array( + 'start' => $first, + 'end' => $last + )); + } + + // Add path into document + static::check_document($_REQUEST['document'], $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']); + + return \EGroupware\Api\Storage\Merge::merge_entries(array_key_exists('0', $ids) ? $ids : $timespan, $document_merge); + } + + public function get_filename_placeholders($document, $ids) + { + $placeholders = parent::get_filename_placeholders($document, $ids); + + $request = json_decode($_REQUEST['id'], true) ?: []; + $template = $ids['view'] ?: ''; + if(stripos($document, 'month') !== false || stripos($document, lang('month')) !== false) + { + $template = 'month'; + } + if(stripos($document, 'week') !== false || stripos($document, lang('week')) !== false) + { + $template = 'week'; + } + + $placeholders['$$span$$'] = lang($template); + $placeholders['$$first$$'] = Api\DateTime::to($ids['first'] ?: $request['first'], true); + $placeholders['$$last$$'] = Api\DateTime::to($ids['last'] ?: $request['last'], true); + $placeholders['$$date$$'] = Api\DateTime::to($ids['date'] ?: $request['date'], true); + + return $placeholders; + } + /** * Get replacements * @@ -182,7 +282,7 @@ class calendar_merge extends Api\Storage\Merge { foreach(self::$range_tags as $key => $format) { - $value = date($format, $key == 'end' ? $id['end'] : $id['start']); + $value = Api\DateTime::to($key == 'end' ? $id['end'] : $id['start'], $format); if($key == 'month') $value = lang($value); $values["$\$range/$key$$"] = $value; } @@ -440,8 +540,11 @@ class calendar_merge extends Api\Storage\Merge } } - $_date = $date['start'] ? $date['start'] : $date; - if($days[date('Ymd',$_date)][$plugin]) return $days[date('Ymd',$_date)][$plugin][$n]; + $_date = new Api\DateTime(['start'] ? $date['start'] : $date); + if($days[$_date->format('Ymd')][$plugin]) + { + return $days[$_date->format('Ymd')][$plugin][$n]; + } $events = $this->bo->search($this->query + array( 'start' => $date['end'] ? $date['start'] : mktime(0, 0, 0, date('m', $_date), date('d', $_date), date('Y', $_date)), @@ -481,7 +584,7 @@ class calendar_merge extends Api\Storage\Merge $replacements['$$calendar_endtime$$'] = date($time_format, $day == date('Ymd', $event['end']) ? $event['end'] : mktime(23, 59, 59, 0, 0, 0)); } - $days[date('Ymd', $_date)][$dow][] = $replacements; + $days[$_date->format('Ymd')][$dow][] = $replacements; } if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false) { @@ -489,24 +592,24 @@ class calendar_merge extends Api\Storage\Merge '$$day/date$$' => date($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'], strtotime($day)), '$$day/name$$' => lang(date('l', strtotime($day))) ); - if(!is_array($days[date('Ymd', $_date)][date('l', strtotime($day))])) + if(!is_array($days[$_date->format('Ymd')][date('l', strtotime($day))])) { $blank = $this->calendar_replacements(array()); foreach($blank as &$value) { $value = ''; } - $days[date('Ymd', $_date)][date('l', strtotime($day))][] = $blank; + $days[$_date->format('Ymd')][date('l', strtotime($day))][] = $blank; } - $days[date('Ymd', $_date)][date('l', strtotime($day))][0] += $date_marker; + $days[$_date->format('Ymd')][date('l', strtotime($day))][0] += $date_marker; } // Add in birthdays if(strpos($repeat, 'day/birthdays') !== false) { - $days[date('Ymd', $_date)][date('l', strtotime($day))][0]['$$day/birthdays$$'] = $this->get_birthdays($day); + $days[$_date->format('Ymd')][date('l', strtotime($day))][0]['$$day/birthdays$$'] = $this->get_birthdays($day); } } - return $days[date('Ymd', $_date)][$plugin][0]; + return $days[$_date->format('Ymd')][$plugin][0]; } /** diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index 8ec58bd39e..9f1f12bdf5 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -534,28 +534,6 @@ class calendar_ui */ function sidebox_etemplate($content = array()) { - if($content['merge']) - { - // View from sidebox is JSON encoded - $this->manage_states(array_merge($content,(array)json_decode($content['view'],true))); - if($content['first']) - { - $this->first = Api\DateTime::to($content['first'],'ts'); - } - if($content['last']) - { - $this->last = new Api\DateTime($content['last']); - $this->last->setTime(23, 59, 59); - $this->last = $this->last->format('ts'); - } - - $_GET['merge'] = $content['merge']; - if($this->merge()) - { - Framework::redirect('/index.php');//, array('menuaction' => 'calendar.calendar_uiviews.index')); - return false; - } - } Etemplate::reset_request(); $sidebox = new Etemplate('calendar.sidebox'); @@ -857,129 +835,4 @@ class calendar_ui } } } - - /** - * Merge calendar events into a document - * - * Checks $_GET['merge'] for the document, and $timespan for the date range. - * If timespan is not provided, we try to guess based on the document name. - * - * @param Array $timespan - * - * @return boolean stop execution - */ - public function merge($timespan = array()) - { - // Merge print - if($_GET['merge']) - { - if(!$timespan) - { - // Try to make time span into appropriate ranges to match - if(stripos($_GET['merge'],'month') !== false || stripos($_GET['merge'],lang('month')) !== false) $template = 'month'; - if(stripos($_GET['merge'],'week') !== false || stripos($_GET['merge'],lang('week')) !== false) $template = 'week'; - //error_log("Detected template $template"); - switch ($template) - { - case 'month': - // Trim to _only_ the month, do not pad to week start / end - $time = new Api\DateTime($this->date); - $timespan = array(array( - 'start' => Api\DateTime::to($time->format('Y-m-01 00:00:00'),'ts'), - 'end' => Api\DateTime::to($time->format('Y-m-t 23:59:59'),'ts') - )); - break; - case 'week': - $timespan = array(); - $start = new Api\DateTime($this->first); - $t = clone $start; - $t->modify('+1 week')->modify('-1 second'); - if($t->format('ts') < Api\DateTime::to($this->last,'ts')) - { - do - { - $timespan[] = array( - 'start' => $start->format('ts'), - 'end' => $t->format('ts') - ); - $start->modify('+1 week'); - $t->modify('+1 week'); - } while( $start->format('ts') < $this->last); - break; - } - // Fall through - default: - $timespan = array(array( - 'start' => is_array($this->first) ? $this->bo->date2ts($this->first) : $this->first, - 'end' => is_array($this->last) ? $this->bo->date2ts($this->last) : $this->last - )); - } - } - $document = $_GET['merge']; - $merge = new calendar_merge(); - if($error = $merge->check_document($document, $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir'])) - { - return $error; - } - if(!$error && $this->merge_collabora($document, $timespan)) - { - return false; - } - else if (!$error) - { - //error_log($_GET['merge'] . ' Timespan: ');foreach($timespan as $t) error_log(Api\DateTime::to($t['start']) . ' - ' . Api\DateTime::to($t['end'])); - $error = $merge->download($document, $timespan, '', $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']); - } - // Here? Doesn't actually give the message - Framework::refresh_opener($error, 'calendar'); - } - unset($_GET['merge']); - if($error) - { - // This doesn't give message either, but at least it doesn't give a blank screen - Framework::redirect_link('/index.php', array( - 'msg' => $error, - 'cd' => 'yes' - )); - } - } - - protected function merge_collabora($document, $timespan) - { - $file = Api\Vfs::stat($document); - if(!$file['mime']) - { - $file['mime'] = Api\Vfs::mime_content_type($document); - } - - $editable_mimes = array(); - try - { - if (class_exists('EGroupware\\collabora\\Bo') && - $GLOBALS['egw_info']['user']['apps']['collabora'] && - $discovery = \EGroupware\collabora\Bo::discover() - ) - { - $editable_mimes = $discovery; - } - } - catch (\Exception $e) - { - return false; - } - $timespan = json_encode($timespan); - - if ($editable_mimes[$file['mime']]) - { - Framework::popup(Framework::link('/index.php', array( - 'menuaction' => 'collabora.EGroupware\\collabora\\Ui.merge_edit', - 'document' => $document, - 'merge' => 'calendar_merge', - 'id' => $timespan - )),'_blank',false); - return true; - } - - return false; - } } diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php index e7e41cba91..8e751b4f4b 100644 --- a/calendar/inc/class.calendar_uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -191,23 +191,6 @@ class calendar_uiviews extends calendar_ui */ function index($content=array()) { - if($content['merge']) - { - // View from sidebox is JSON encoded - $this->manage_states(array_merge($content,json_decode($content['view'],true))); - if($content['first']) - { - $this->first = Api\DateTime::to($content['first'],'ts'); - } - if($content['last']) - { - $this->last = Api\DateTime::to($content['last'],'ts'); - } - $_GET['merge'] = $content['merge']; - $this->merge(); - return; - } - // handle views in other files if (!isset($this->public_functions[$this->view]) && $this->view !== 'listview') { @@ -499,16 +482,6 @@ class calendar_uiviews extends calendar_ui $this->last = $this->bo->date2ts($this->last); } - $merge = $this->merge(); - if($merge) - { - Egw::redirect_link('/index.php',array( - 'menuaction' => 'calendar.calendar_uiviews.index', - 'msg' => $merge, - 'ajax' => 'true' - )); - } - $search_params = $this->search_params; $search_params['daywise'] = false; $search_params['start'] = $this->first; diff --git a/calendar/js/app.ts b/calendar/js/app.ts index e85f168a81..3d5c4721b1 100644 --- a/calendar/js/app.ts +++ b/calendar/js/app.ts @@ -2533,7 +2533,7 @@ export class CalendarApp extends EgwApp } if(action && selected) { - action.execute(selected); + super.merge(action, selected); } } else @@ -2541,16 +2541,23 @@ export class CalendarApp extends EgwApp // Set the hidden inputs to the current time span & submit widget.getRoot().getWidgetById('first').set_value(app.calendar.state.first); widget.getRoot().getWidgetById('last').set_value(app.calendar.state.last); - if(widget.getRoot().getArrayMgr('content').getEntry('collabora_enabled')) - { - widget.getInstanceManager().submit(); - } - else - { - widget.getInstanceManager().postSubmit(); - window.setTimeout(function() {widget.set_value('');},100); - } + + let vars = { + menuaction: 'calendar.calendar_merge.merge_entries', + document: widget.getValue(), + merge: 'calendar_merge', + pdf: false, + select_all: false, + id: JSON.stringify({ + first: app.calendar.state.first, + last: app.calendar.state.last, + date: app.calendar.state.first, + view: app.calendar.state.view + }) + }; + egw.open_link(egw.link('/index.php', vars), '_blank'); } + widget.set_value(''); return false; } From ceb014200ffb343d590e8a1f3538a0e87c276072 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 24 Nov 2021 09:23:07 -0700 Subject: [PATCH 19/19] Calendar: If an event moves from the past to the future, send notifications according to preferences Previously we ignored all notifications, because the event _was_ in the past. --- calendar/inc/class.calendar_boupdate.inc.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 0116a7a611..a55938b069 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -875,23 +875,25 @@ class calendar_boupdate extends calendar_bo $disinvited = $msg_type == MSG_DISINVITE ? array_keys($to_notify) : array(); $owner = $old_event ? $old_event['owner'] : $new_event['owner']; - if ($owner && !isset($to_notify[$owner]) && $msg_type != MSG_ALARM) + if($owner && !isset($to_notify[$owner]) && $msg_type != MSG_ALARM) { - $to_notify[$owner] = 'OCHAIR'; // always include the event-owner + $to_notify[$owner] = 'OCHAIR'; // always include the event-owner } // ignore events in the past (give a tolerance of 10 seconds for the script) - if($old_event && $this->date2ts($old_event['start']) < ($this->now_su - 10)) + if($new_event && $this->date2ts($new_event['start']) < ($this->now_su - 10) || + !$new_event && $old_event && $this->date2ts($old_event['start']) < ($this->now_su - 10) + ) { return False; } // check if default timezone is set correctly to server-timezone (ical-parser messes with it!!!) - if ($GLOBALS['egw_info']['server']['server_timezone'] && ($tz = date_default_timezone_get()) != $GLOBALS['egw_info']['server']['server_timezone']) + if($GLOBALS['egw_info']['server']['server_timezone'] && ($tz = date_default_timezone_get()) != $GLOBALS['egw_info']['server']['server_timezone']) { $restore_tz = $tz; date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); } - $temp_user = $GLOBALS['egw_info']['user']; // save user-date of the enviroment to restore it after + $temp_user = $GLOBALS['egw_info']['user']; // save user-date of the enviroment to restore it after if (!$user) {