mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-24 17:04:14 +01:00
Merge branch 'master' into web-components
This commit is contained in:
commit
e03b8bbdc9
@ -98,8 +98,11 @@ class addressbook_groupdav extends Api\CalDAV\Handler
|
||||
}
|
||||
if ($this->debug) error_log(__METHOD__."() contact_repository={$this->bo->contact_repository}, account_repository={$this->bo->account_repository}, REQUEST_URI=$_SERVER[REQUEST_URI] --> path_attr=".self::$path_attr.", path_extension=".self::$path_extension);
|
||||
|
||||
$this->home_set_pref = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'];
|
||||
$this->home_set_pref = $this->home_set_pref ? explode(',',$this->home_set_pref) : array();
|
||||
$this->home_set_pref = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'] ?? [];
|
||||
if (!is_array($this->home_set_pref))
|
||||
{
|
||||
$this->home_set_pref = $this->home_set_pref ? explode(',', $this->home_set_pref) : array();
|
||||
}
|
||||
|
||||
// silently switch "Sync all into one" preference on for OS X addressbook, as it only supports one AB
|
||||
// this restores behavior before Lion (10.7), where AB synced all ABs contained in addressbook-home-set
|
||||
|
@ -193,7 +193,7 @@ class admin_acl
|
||||
}
|
||||
else
|
||||
{
|
||||
$check = array_diff($memberships, $groups);
|
||||
$check = array_diff($memberships, (array)$groups);
|
||||
//error_log(__METHOD__."() app=$app, array_diff(memberships=".array2string($memberships).", groups=".array2string($groups).")=".array2string($check));
|
||||
if (!$check) continue; // would give sql error otherwise!
|
||||
}
|
||||
|
@ -594,7 +594,7 @@ class Ldap
|
||||
{
|
||||
// dont convert the (binary) jpegPhoto!
|
||||
$ldapContact[$ldapFieldName] = $ldapFieldName == 'jpegphoto' ? $data[$egwFieldName] :
|
||||
Api\Translation::convert(trim($data[$egwFieldName]),$this->charset,'utf-8');
|
||||
Api\Translation::convert($data[$egwFieldName], $this->charset,'utf-8');
|
||||
}
|
||||
elseif($isUpdate && isset($data[$egwFieldName]))
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ class File extends Etemplate\Widget
|
||||
*/
|
||||
public function __construct($xml='')
|
||||
{
|
||||
$this->bool_attr_default += array(
|
||||
$this->bool_attr_default = ($this->bool_attr_default ?? []) + array(
|
||||
'multiple' => false,
|
||||
);
|
||||
|
||||
|
@ -998,7 +998,7 @@ abstract class Ajax extends Api\Framework
|
||||
{
|
||||
$open_tabs = $GLOBALS['egw_info']['user']['preferences']['common']['open_tabs'];
|
||||
}
|
||||
$open_tabs = $open_tabs ? explode(',',$open_tabs) : array();
|
||||
$open_tabs = $open_tabs && is_string($open_tabs) ? explode(',',$open_tabs) : (array)$open_tabs;
|
||||
if ($active_tab && !in_array($active_tab,$open_tabs))
|
||||
{
|
||||
$open_tabs[] = $active_tab;
|
||||
|
@ -3067,7 +3067,7 @@ class Mail
|
||||
$folders = $this->icServer->getMailboxes($path, $_search, true);
|
||||
}
|
||||
|
||||
uasort($folders,array($this,'sortByMailbox'));//ksort($folders);
|
||||
if (is_array($folders)) uasort($folders, array($this,'sortByMailbox'));
|
||||
}
|
||||
elseif(!$_nodePath) // all
|
||||
{
|
||||
@ -3088,7 +3088,7 @@ class Mail
|
||||
if (self::$debugTimes) $starttime = microtime (true);
|
||||
// Merge of all auto folders and specialusefolders
|
||||
$autoFoldersTmp = array_unique((array_merge(self::$autoFolders, array_values(self::$specialUseFolders))));
|
||||
uasort($folders,array($this,'sortByMailbox'));//ksort($folders);
|
||||
if (is_array($folders)) uasort($folders,array($this,'sortByMailbox'));//ksort($folders);
|
||||
$tmpFolders = $folders;
|
||||
$inboxFolderObject=$inboxSubFolderObjects=$autoFolderObjects=$typeFolderObject=$mySpecialUseFolders=array();
|
||||
$googleMailFolderObject=$googleAutoFolderObjects=$googleSubFolderObjects=array();
|
||||
@ -5062,7 +5062,7 @@ class Mail
|
||||
static function &getdisplayableBody(&$mailClass, $bodyParts, $preserveHTML = false, $useTidy = true)
|
||||
{
|
||||
$message='';
|
||||
for($i=0, $cnt=count($bodyParts); $i < $cnt; $i++)
|
||||
for($i=0, $cnt=count($bodyParts ?? []); $i < $cnt; $i++)
|
||||
{
|
||||
if (!isset($bodyParts[$i]['body'])) {
|
||||
$bodyParts[$i]['body'] = self::getdisplayableBody($mailClass, $bodyParts[$i], $preserveHTML, $useTidy);
|
||||
|
@ -555,7 +555,7 @@ class Html
|
||||
$searchFor = '<pre>';
|
||||
$pos = stripos($html,$searchFor);
|
||||
}
|
||||
if ($pos === false)
|
||||
if ($pos === false || !$html)
|
||||
{
|
||||
return $html;
|
||||
}
|
||||
@ -564,7 +564,11 @@ class Html
|
||||
{
|
||||
// avoid infinit loop in case the endof pre can't be found, just give the
|
||||
// end position to return the rest of content as return html
|
||||
$endofpre = (stripos($html,'</pre>',$pos) === false ? strlen($html) : stripos($html,'</pre>',$pos));
|
||||
if (($endofpre = stripos($html, '</pre>', $pos)) === false)
|
||||
{
|
||||
$html2ret[] = substr($html, $pos);
|
||||
break;
|
||||
}
|
||||
$length = $endofpre-$pos+6;
|
||||
$html2ret[] = substr($html,$pos,$length);
|
||||
$searchFor = '<pre ';
|
||||
|
@ -632,7 +632,7 @@ class Base
|
||||
{
|
||||
continue; // no need to write that (unset) column
|
||||
}
|
||||
if ($this->table_def['fd'][$db_col]['type'] == 'varchar' &&
|
||||
if ($this->table_def['fd'][$db_col]['type'] == 'varchar' && is_string($this->data[$col]) &&
|
||||
strlen($this->data[$col]) > $this->table_def['fd'][$db_col]['precision'])
|
||||
{
|
||||
// truncate the field to mamimum length, if upper layers didn't care
|
||||
|
@ -1550,7 +1550,7 @@ class calendar_boupdate extends calendar_bo
|
||||
$event['created'] = $save_event['created'] = $this->now;
|
||||
$event['creator'] = $save_event['creator'] = $this->user;
|
||||
}
|
||||
$set_recurrences = $old_event ? abs($event['recur_enddate'] - $old_event['recur_enddate']) > 1 : false;
|
||||
$set_recurrences = $old_event ? abs(Api\DateTime::to($event['recur_enddate'], 'utc') - Api\DateTime::to($old_event['recur_enddate'], 'utc')) > 1 : false;
|
||||
$set_recurrences_start = 0;
|
||||
if (($cal_id = $this->so->save($event,$set_recurrences,$set_recurrences_start,0,$event['etag'])) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE)
|
||||
{
|
||||
@ -1945,7 +1945,8 @@ class calendar_boupdate extends calendar_bo
|
||||
if (!($exception = $this->read($id))) continue;
|
||||
$exception['uid'] = Api\CalDAV::generate_uid('calendar', $id);
|
||||
$exception['reference'] = $exception['recurrence'] = 0;
|
||||
$this->update($exception, true, true, false, true, $msg=null, true);
|
||||
$msg = null;
|
||||
$this->update($exception, true, true, false, true, $msg, true);
|
||||
++$exceptions_kept;
|
||||
}
|
||||
}
|
||||
|
@ -928,7 +928,7 @@ class calendar_ical extends calendar_boupdate
|
||||
|
||||
if ($alarmData['offset'])
|
||||
{
|
||||
$alarmData['time'] = $event['start'] - $alarmData['offset'];
|
||||
$alarmData['time'] = Api\DateTime::to($event['start'], 'ts') - $alarmData['offset'];
|
||||
}
|
||||
|
||||
$description = trim(preg_replace("/\r?\n?\\[[A-Z_]+:.*\\]/i", '', $event['description']));
|
||||
|
@ -2844,7 +2844,7 @@ ORDER BY cal_user_type, cal_usre_id
|
||||
$days[$locts]= $locts;
|
||||
}
|
||||
}
|
||||
elseif (($filter == 'map' || filter == 'tz_map') &&
|
||||
elseif (($filter == 'map' || $filter == 'tz_map') &&
|
||||
!$tz_exception)
|
||||
{
|
||||
// no pseudo exception date
|
||||
|
@ -984,7 +984,8 @@ class calendar_uiforms extends calendar_ui
|
||||
$exception['recurrence'] += $offset;
|
||||
$exception['reference'] = $event['id'];
|
||||
$exception['uid'] = $event['uid'];
|
||||
$this->bo->update($exception, true, true, true, true, $msg=null, $content['no_notifications']);
|
||||
$msg = null;
|
||||
$this->bo->update($exception, true, true, true, true, $msg, $content['no_notifications']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1495,9 +1496,6 @@ class calendar_uiforms extends calendar_ui
|
||||
*/
|
||||
public function ajax_conflicts()
|
||||
{
|
||||
$participants = json_decode($_GET['participants'],true);
|
||||
unset($_GET['participants']);
|
||||
|
||||
$content = $this->default_add_event();
|
||||
|
||||
// Process edit wants to see input values
|
||||
|
@ -94,8 +94,7 @@
|
||||
"egroupware/bookmarks": "self.version",
|
||||
"egroupware/collabora": "self.version",
|
||||
"egroupware/compress": "^2.2.3",
|
||||
"egroupware/crypt": "^2.7.13",
|
||||
"egroupware/guzzlestream": "dev-master",
|
||||
"egroupware/crypt": "^2.7.14",
|
||||
"egroupware/icalendar": "^2.1.9",
|
||||
"egroupware/imap-client": "2.30.4",
|
||||
"egroupware/magicsuggest": "^2.1",
|
||||
@ -108,7 +107,7 @@
|
||||
"egroupware/status": "self.version",
|
||||
"egroupware/swoolepush": "self.version",
|
||||
"egroupware/tracker": "self.version",
|
||||
"egroupware/webdav": "^v0.3.1",
|
||||
"egroupware/webdav": "^v0.3.2",
|
||||
"egroupware/z-push-dev": "^2.5",
|
||||
"giggsey/libphonenumber-for-php": "^8.12",
|
||||
"npm-asset/as-jqplot": "1.0.*",
|
||||
@ -129,7 +128,7 @@
|
||||
"robrichards/xmlseclibs": "^3.1.1",
|
||||
"simplesamlphp/simplesamlphp": "^1.19.0",
|
||||
"simplesamlphp/twig-configurable-i18n": "~2.3.3",
|
||||
"tinymce/tinymce": "5.9.*"
|
||||
"tinymce/tinymce": "5.10.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
|
161
composer.lock
generated
161
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5a9ac5a19bb28961cbb8215f95ea72b9",
|
||||
"content-hash": "de144b0b6d74a96e1d0fa87a0ece2b7c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adldap2/adldap2",
|
||||
@ -1032,16 +1032,16 @@
|
||||
},
|
||||
{
|
||||
"name": "egroupware/crypt",
|
||||
"version": "2.7.13",
|
||||
"version": "2.7.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EGroupware/Crypt.git",
|
||||
"reference": "d25af6514c134426647dae2c47e5bf07bb7c4d1e"
|
||||
"reference": "ae2d6bfc81b1c820183c355ae100f60d3133f47e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/EGroupware/Crypt/zipball/d25af6514c134426647dae2c47e5bf07bb7c4d1e",
|
||||
"reference": "d25af6514c134426647dae2c47e5bf07bb7c4d1e",
|
||||
"url": "https://api.github.com/repos/EGroupware/Crypt/zipball/ae2d6bfc81b1c820183c355ae100f60d3133f47e",
|
||||
"reference": "ae2d6bfc81b1c820183c355ae100f60d3133f47e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1088,22 +1088,84 @@
|
||||
"description": "Cryptography library",
|
||||
"homepage": "https://www.horde.org/libraries/Horde_Crypt",
|
||||
"support": {
|
||||
"source": "https://github.com/EGroupware/Crypt/tree/v2.7.13"
|
||||
"source": "https://github.com/EGroupware/Crypt/tree/2.7.14"
|
||||
},
|
||||
"time": "2017-11-11T00:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "egroupware/guzzlehttp",
|
||||
"version": "3.9.3",
|
||||
"target-dir": "Guzzle/Http",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EGroupware/guzzlehttp.git",
|
||||
"reference": "761951f0cb98109275cfd95776ab3725a903389c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/EGroupware/guzzlehttp/zipball/761951f0cb98109275cfd95776ab3725a903389c",
|
||||
"reference": "761951f0cb98109275cfd95776ab3725a903389c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"egroupware/guzzlestream": "^3.9.3",
|
||||
"guzzle/common": "^3.9.2",
|
||||
"guzzle/parser": "^3.9.2",
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"replace": {
|
||||
"guzzle/http": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Guzzle\\Http": ""
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "HTTP libraries used by Guzzle",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"Guzzle",
|
||||
"client",
|
||||
"curl",
|
||||
"http",
|
||||
"http client"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/EGroupware/guzzlehttp/tree/3.9.3"
|
||||
},
|
||||
"time": "2021-11-03T17:38:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "egroupware/guzzlestream",
|
||||
"version": "dev-master",
|
||||
"version": "3.9.3",
|
||||
"target-dir": "Guzzle/Stream",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EGroupware/stream.git",
|
||||
"url": "https://github.com/EGroupware/guzzlestream.git",
|
||||
"reference": "d29fc35ebf3bd752308520aa5f17a3e5500f6af3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/EGroupware/stream/zipball/d29fc35ebf3bd752308520aa5f17a3e5500f6af3",
|
||||
"url": "https://api.github.com/repos/EGroupware/guzzlestream/zipball/d29fc35ebf3bd752308520aa5f17a3e5500f6af3",
|
||||
"reference": "d29fc35ebf3bd752308520aa5f17a3e5500f6af3",
|
||||
"shasum": ""
|
||||
},
|
||||
@ -1695,20 +1757,20 @@
|
||||
},
|
||||
{
|
||||
"name": "egroupware/webdav",
|
||||
"version": "v0.3.1",
|
||||
"version": "0.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EGroupware/WebDAV.git",
|
||||
"reference": "554b8ed3fb3bc98427f0c1edbba7f9bab9894d4c"
|
||||
"reference": "639ea0b979ffb75107bcc7f8a37a74acc9410720"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/EGroupware/WebDAV/zipball/554b8ed3fb3bc98427f0c1edbba7f9bab9894d4c",
|
||||
"reference": "554b8ed3fb3bc98427f0c1edbba7f9bab9894d4c",
|
||||
"url": "https://api.github.com/repos/EGroupware/WebDAV/zipball/639ea0b979ffb75107bcc7f8a37a74acc9410720",
|
||||
"reference": "639ea0b979ffb75107bcc7f8a37a74acc9410720",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzle/http": "~3.0",
|
||||
"egroupware/guzzlehttp": "^3.9.3",
|
||||
"php": ">=5.3.0",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
@ -1743,7 +1805,7 @@
|
||||
"stream",
|
||||
"wrapper"
|
||||
],
|
||||
"time": "2021-09-10T11:59:20+00:00"
|
||||
"time": "2021-11-03T18:16:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "egroupware/z-push-dev",
|
||||
@ -2106,64 +2168,6 @@
|
||||
"abandoned": "guzzle/guzzle",
|
||||
"time": "2014-08-11T04:32:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzle/http",
|
||||
"version": "v3.9.2",
|
||||
"target-dir": "Guzzle/Http",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Guzzle3/http.git",
|
||||
"reference": "1e8dd1e2ba9dc42332396f39fbfab950b2301dc5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Guzzle3/http/zipball/1e8dd1e2ba9dc42332396f39fbfab950b2301dc5",
|
||||
"reference": "1e8dd1e2ba9dc42332396f39fbfab950b2301dc5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzle/common": "self.version",
|
||||
"guzzle/parser": "self.version",
|
||||
"guzzle/stream": "self.version",
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Guzzle\\Http": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "HTTP libraries used by Guzzle",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"Guzzle",
|
||||
"client",
|
||||
"curl",
|
||||
"http",
|
||||
"http client"
|
||||
],
|
||||
"abandoned": "guzzle/guzzle",
|
||||
"time": "2014-08-11T04:32:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzle/parser",
|
||||
"version": "v3.9.2",
|
||||
@ -8495,16 +8499,16 @@
|
||||
},
|
||||
{
|
||||
"name": "tinymce/tinymce",
|
||||
"version": "5.9.2",
|
||||
"version": "5.10.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tinymce/tinymce-dist.git",
|
||||
"reference": "48c665ad12ba0e4d8068ba0784026c7488aa4746"
|
||||
"reference": "23dbb5d218707805b74c9fe4f1e2525bcd21e689"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tinymce/tinymce-dist/zipball/48c665ad12ba0e4d8068ba0784026c7488aa4746",
|
||||
"reference": "48c665ad12ba0e4d8068ba0784026c7488aa4746",
|
||||
"url": "https://api.github.com/repos/tinymce/tinymce-dist/zipball/23dbb5d218707805b74c9fe4f1e2525bcd21e689",
|
||||
"reference": "23dbb5d218707805b74c9fe4f1e2525bcd21e689",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "component",
|
||||
@ -8545,7 +8549,7 @@
|
||||
"tinymce",
|
||||
"wysiwyg"
|
||||
],
|
||||
"time": "2021-09-08T03:45:09+00:00"
|
||||
"time": "2021-11-03T03:57:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/extensions",
|
||||
@ -10731,7 +10735,6 @@
|
||||
"egroupware/adodb-php": 20,
|
||||
"egroupware/bookmarks": 20,
|
||||
"egroupware/collabora": 20,
|
||||
"egroupware/guzzlestream": 20,
|
||||
"egroupware/news_admin": 20,
|
||||
"egroupware/openid": 20,
|
||||
"egroupware/projectmanager": 20,
|
||||
|
@ -2220,7 +2220,7 @@ class infolog_ui
|
||||
|
||||
$content['info_anz_subs'] = (int)$content['info_anz_subs']; // gives javascript error if empty!
|
||||
|
||||
$old_pm_id = is_array($pm_links) ? array_shift($pm_links) : $content['old_pm_id'];
|
||||
$old_pm_id = $content['pm_id'] ?: (is_array($pm_links) ? array_shift($pm_links) : $content['old_pm_id']);
|
||||
unset($content['old_pm_id']);
|
||||
|
||||
if ($info_id && $this->bo->history)
|
||||
|
312
infolog/tests/DoubleLinkPMTest.php
Normal file
312
infolog/tests/DoubleLinkPMTest.php
Normal file
@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
namespace EGroupware\Infolog;
|
||||
|
||||
|
||||
use EGroupware\Api\Categories;
|
||||
use EGroupware\Api\Etemplate;
|
||||
use Egroupware\Api\Link;
|
||||
use EGroupware\Api\TestEtemplate;
|
||||
|
||||
require_once realpath(__DIR__ . '/../../api/tests/EtemplateTest.php');
|
||||
|
||||
/**
|
||||
* Test having an infolog that is part of a project, and linking it to another project
|
||||
*
|
||||
* Tests to make sure that the original project stays as _the_ project (pm_id), but that
|
||||
* both projects get linked to the infolog, and the infolog is an element of both projects.
|
||||
*
|
||||
* Since projectmanager is involved, there are frequent calls to Api\Link::run_notifies()
|
||||
* to keep its elements up to date. Normally this is not needed, as it is run
|
||||
* at the end of every execution.
|
||||
*/
|
||||
class DoubleLinkPMTest extends \EGroupware\Api\EtemplateTest
|
||||
{
|
||||
|
||||
protected $ui;
|
||||
protected $bo;
|
||||
protected $pm_bo;
|
||||
|
||||
// Infolog under test
|
||||
protected $info_id = null;
|
||||
|
||||
// Project(s) used to test
|
||||
protected $pm_id = [];
|
||||
|
||||
protected function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->ui = new \infolog_ui();
|
||||
|
||||
$this->ui->tmpl = $this->createPartialMock(Etemplate::class, array('exec'));
|
||||
$this->ui->tmpl->expects($this->any())
|
||||
->method('exec')
|
||||
->will($this->returnCallback([$this, 'mockExec']));
|
||||
|
||||
$this->bo = $this->ui->bo;
|
||||
$this->pm_bo = new \projectmanager_bo();
|
||||
|
||||
$this->bo->tracking = $this->createStub(\infolog_tracking::class);
|
||||
$this->bo->tracking->method('track')->willReturn(0);
|
||||
|
||||
// Make sure projects are not there first
|
||||
$pm_numbers = array(
|
||||
'TEST 1',
|
||||
'TEST 2'
|
||||
);
|
||||
foreach($pm_numbers as $number)
|
||||
{
|
||||
$project = $this->pm_bo->read(array('pm_number' => $number));
|
||||
if($project && $project['pm_id'])
|
||||
{
|
||||
$this->pm_bo->delete($project);
|
||||
}
|
||||
}
|
||||
|
||||
$this->makeProject("1");
|
||||
|
||||
// Make another project, we need 2
|
||||
$this->pm_bo->data = array();
|
||||
$this->makeProject("2");
|
||||
}
|
||||
|
||||
protected function tearDown() : void
|
||||
{
|
||||
// Remove infolog under test
|
||||
if($this->info_id)
|
||||
{
|
||||
$this->bo->delete($this->info_id, False, False, True);
|
||||
// One more time for history
|
||||
$this->bo->delete($this->info_id, False, False, True);
|
||||
}
|
||||
|
||||
// Remove the test project
|
||||
$this->deleteProject();
|
||||
|
||||
$this->bo = null;
|
||||
$this->pm_bo = null;
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Have an infolog that is part of a project, but then add it into another project.
|
||||
* We expect both projects to stay linked, but _the_ project to be unchanged.
|
||||
* It works correctly if you debug it but not if you run it
|
||||
*/
|
||||
public function testInProjectLinkAnother()
|
||||
{
|
||||
$first_project = $this->pm_id[0];
|
||||
$second_project = $this->pm_id[1];
|
||||
|
||||
$info = $this->getTestInfolog();
|
||||
// Set project by ID
|
||||
//$info['pm_id'] = $first_project;
|
||||
|
||||
$this->info_id = $this->bo->write($info);
|
||||
$this->assertIsInt($this->info_id);
|
||||
$this->assertGreaterThan(0, $this->info_id);
|
||||
|
||||
// Force links to run notification now so we get valid testing - it
|
||||
// usually waits until Egw::on_shutdown();
|
||||
Link::run_notifies();
|
||||
|
||||
// Fake opening the edit dialog, important not to pass an array to accurately copy normal behaviour
|
||||
$this->ui->edit($this->info_id);
|
||||
// Set button 'apply' to save, but not try to close the window since
|
||||
// that would fail
|
||||
$content = self::$mocked_exec_result;
|
||||
|
||||
$content['pm_id'] = $first_project;
|
||||
$content['button'] = array('apply' => true);
|
||||
$this->ui->edit($content);
|
||||
|
||||
// Force links to run notification now so we get valid testing - it
|
||||
// usually waits until Egw::on_shutdown();
|
||||
Link::run_notifies();
|
||||
// Now load it again
|
||||
$info = $this->bo->read($this->info_id);
|
||||
|
||||
// Check original pm_id is there
|
||||
$this->assertNotNull($info['pm_id'], 'Project was not set');
|
||||
$this->assertEquals($first_project, $info['pm_id'], 'Project went missing');
|
||||
|
||||
// Now link another project
|
||||
Link::link('infolog', $this->info_id, 'projectmanager', $second_project, "This is the second project");
|
||||
|
||||
// Force links to run notification now so we get valid testing - it
|
||||
// usually waits until Egw::on_shutdown();
|
||||
Link::run_notifies();
|
||||
$this->check_links($this->info_id, $first_project, $second_project);
|
||||
|
||||
// Make a call to edit, looks like user updated subject and clicked Apply
|
||||
// Ticket #63114 says an edit will remove the second project
|
||||
$this->loadAndCheckLinks($info, $first_project, $second_project);
|
||||
|
||||
// Check infolog is in original project
|
||||
$this->checkElements($first_project);
|
||||
// Check infolog is in second project
|
||||
$this->checkElements($second_project);
|
||||
}
|
||||
|
||||
public function mockExec($method, $content, $sel_options, $readonlys, $preserve)
|
||||
{
|
||||
$tmpl = new Etemplate('infolog.edit');
|
||||
$GLOBALS['egw']->categories = new Categories('', 'infolog');
|
||||
$result = parent::mockedExec($tmpl, $content, $sel_options, $readonlys, $preserve);
|
||||
// Check for the load
|
||||
$data = array();
|
||||
foreach($result as $command)
|
||||
{
|
||||
if($command['type'] == 'et2_load')
|
||||
{
|
||||
$data = $command['data'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], true);
|
||||
|
||||
$content = static::$mocked_exec_result;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load infolog via etemplate & check links & pm_id are still there
|
||||
*
|
||||
* @param $pm_id_1
|
||||
* @param $pm_id_2
|
||||
*/
|
||||
protected function loadAndCheckLinks($info, $pm_id_1, $pm_id_2)
|
||||
{
|
||||
// Fake opening the edit dialog, important not to pass an array to accurately copy normal behaviour
|
||||
$this->ui->edit($info['info_id']);
|
||||
|
||||
// Set button 'apply' to save, but not try to close the window since
|
||||
// that would fail
|
||||
$content = self::$mocked_exec_result;
|
||||
|
||||
$content['info_subject'] .= " save #1";
|
||||
$content['button'] = array('apply' => true);
|
||||
$this->ui->edit($content);
|
||||
|
||||
// Now do it again
|
||||
// Ticket #63114 says a second edit will remove the second project
|
||||
$content = self::$mocked_exec_result;
|
||||
|
||||
$content['info_subject'] .= " save #2";
|
||||
$content['button'] = array('apply' => true);
|
||||
$this->ui->edit($content);
|
||||
|
||||
$info = $this->bo->read($this->info_id);
|
||||
|
||||
// Check original pm_id is there
|
||||
$this->assertNotNull($info['pm_id'], 'Original project was not set');
|
||||
$this->assertEquals($pm_id_1, $info['pm_id'], 'Original project went missing');
|
||||
|
||||
$this->check_links($this->info_id, $pm_id_1, $pm_id_2);
|
||||
}
|
||||
|
||||
protected function check_links($info_id, $pm_id_1, $pm_id_2)
|
||||
{
|
||||
// Check links
|
||||
$links = Link::get_links('infolog', $info_id);
|
||||
$all_there = count(array_filter($links, function ($link) use ($pm_id_1)
|
||||
{
|
||||
return $link['id'] == $pm_id_1;
|
||||
})
|
||||
) == 1;
|
||||
$this->assertTrue($all_there, "First project went missing");
|
||||
$all_there = count(array_filter($links, function ($link) use ($pm_id_2)
|
||||
{
|
||||
return $link['id'] == $pm_id_2;
|
||||
})
|
||||
) == 1;
|
||||
$this->assertTrue($all_there, "Second project went missing");
|
||||
}
|
||||
|
||||
protected function getTestInfolog()
|
||||
{
|
||||
return array(
|
||||
'info_subject' => 'Test Infolog Entry for ' . $this->getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a project so we can test deleting it
|
||||
*/
|
||||
protected function makeProject($pm_number = '')
|
||||
{
|
||||
$project = array(
|
||||
'pm_number' => 'TEST' . ($pm_number ? " $pm_number" : ''),
|
||||
'pm_title' => 'Auto-test for ' . $this->getName(),
|
||||
'pm_status' => 'active',
|
||||
'pm_description' => 'Test project for ' . $this->getName()
|
||||
);
|
||||
|
||||
// Save & set modifier, no notifications
|
||||
try
|
||||
{
|
||||
$result = true;
|
||||
$result = $this->pm_bo->save($project, true, false);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Something went wrong, we'll just fail
|
||||
$this->fail($e);
|
||||
}
|
||||
|
||||
$this->assertFalse((boolean)$result, 'Error making test project');
|
||||
$this->assertArrayHasKey('pm_id', $this->pm_bo->data, 'Could not make test project');
|
||||
$this->assertThat($this->pm_bo->data['pm_id'],
|
||||
$this->logicalAnd(
|
||||
$this->isType('integer'),
|
||||
$this->greaterThan(0)
|
||||
)
|
||||
);
|
||||
$this->pm_id[] = $this->pm_bo->data['pm_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the project element is present
|
||||
*
|
||||
*/
|
||||
protected function checkElements($pm_id, $expected_count = 1)
|
||||
{
|
||||
$element_bo = new \projectmanager_elements_bo();
|
||||
$element_count = 0;
|
||||
|
||||
foreach((array)$element_bo->search(array('pm_id' => $pm_id), false) as $element)
|
||||
{
|
||||
$element_count++;
|
||||
$this->assertEquals($this->info_id, $element['pe_app_id']);
|
||||
}
|
||||
|
||||
$this->assertEquals($expected_count, $element_count, "Incorrect number of elements");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully delete a project and its elements, no matter what state or settings
|
||||
*/
|
||||
protected function deleteProject()
|
||||
{
|
||||
// Force links to run notification now, or elements might stay
|
||||
// usually waits until Egw::on_shutdown();
|
||||
Link::run_notifies();
|
||||
|
||||
// Force to ignore setting
|
||||
$this->pm_bo->history = '';
|
||||
foreach($this->pm_id as $pm_id)
|
||||
{
|
||||
$this->pm_bo->delete($pm_id, true);
|
||||
}
|
||||
|
||||
// Force links to run notification now, or elements might stay
|
||||
// usually waits until Egw::on_shutdown();
|
||||
Link::run_notifies();
|
||||
}
|
||||
|
||||
}
|
@ -2347,8 +2347,8 @@ class mail_compose
|
||||
@htmlspecialchars(lang("date").": ".Mail::_strtotime($headers['DATE'],'r',true), ENT_QUOTES | ENT_IGNORE,Mail::$displayCharset, false)."\r\n".
|
||||
'-------------------------------------------------'."\r\n \r\n ";
|
||||
$this->sessionData['mimeType'] = 'plain';
|
||||
|
||||
for($i=0; $i<count($bodyParts); $i++) {
|
||||
$countBodyParts = count((array)$bodyParts);
|
||||
for($i=0; $i<$countBodyParts; $i++) {
|
||||
if($i>0) {
|
||||
$this->sessionData['body'] .= "<hr>";
|
||||
}
|
||||
|
@ -2291,8 +2291,8 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
|
||||
$content['mailDisplayBodySrc'] = Egw::link('/index.php',$linkData);
|
||||
$content['mail_displayattachments'] = $attachmentHTMLBlock;
|
||||
$content['mail_id']=$rowID;
|
||||
$content['mailDisplayContainerClass']=(count($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight");
|
||||
$content['mailDisplayAttachmentsClass']=(count($attachments)?"mailDisplayAttachments":"mail_DisplayNone");
|
||||
$content['mailDisplayContainerClass']=!empty($attachments)?"mailDisplayContainer mailDisplayContainerFixedHeight":"mailDisplayContainer mailDisplayContainerFullHeight";
|
||||
$content['mailDisplayAttachmentsClass']=!empty($attachments)?"mailDisplayAttachments":"mail_DisplayNone";
|
||||
|
||||
// DRAG attachments actions
|
||||
$etpl->setElementAttribute('mail_displayattachments', 'actions', array(
|
||||
|
Loading…
Reference in New Issue
Block a user