* Adressbook: phone-number in search only returns contacts having that (normalized) number

This commit is contained in:
Ralf Becker 2021-02-14 14:06:01 +02:00
parent e89fe55882
commit f8ec1618d9
3 changed files with 246 additions and 345 deletions

View File

@ -17,6 +17,7 @@
namespace EGroupware\Api;
use calendar_bo; // to_do: do NOT require it, just use if there
use libphonenumber\PhoneNumberUtil;
/**
* Business object for contacts
@ -2753,4 +2754,111 @@ class Contacts extends Contacts\Storage
}
Egw::redirect(\EGroupware\Api\Image::find('addressbook','photo'));
}
/**
* Regular expression to search for an exact phone number match instead of regular search
*
* Requires a leading + or 0 and only numbers (ignores /-() and space) plus minimum length of 9 chars
*/
const PHONE_PREG = '/^(\+|0)[0-9 ()\/-]{8,}$/';
/**
* searches db for rows matching searchcriteria
*
* '*' and '?' are replaced with sql-wildcards '%' and '_'
*
* @param array|string $criteria array of key and data cols, OR string to search over all standard search fields
* @param boolean|string $only_keys =true True returns only keys, False returns all cols. comma seperated list of keys to return
* @param string $order_by ='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY)
* @param string|array $extra_cols ='' string or array of strings to be added to the SELECT, eg. "count(*) as num"
* @param string $wildcard ='' appended befor and after each criteria
* @param boolean $empty =false False=empty criteria are ignored in query, True=empty have to be empty in row
* @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
* @param mixed $start =false if != false, return only maxmatch rows begining with start, or array($start,$num)
* @param array $filter =null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards
* $filter['cols_to_search'] limit search columns to given columns, otherwise $this->columns_to_search is used
* @param string $join ='' sql to do a join (only used by sql backend!), eg. " RIGHT JOIN egw_accounts USING(account_id)"
* @param boolean $ignore_acl =false true: no acl check
* @return array of matching rows (the row is an array of the cols) or False
*/
function &search($criteria, $only_keys = True, $order_by = '', $extra_cols = '', $wildcard = '', $empty = False, $op = 'AND', $start = false, $filter = null, $join = '', $ignore_acl = false)
{
if (is_string($criteria) && preg_match(self::PHONE_PREG, $criteria))
{
try {
return $this->phone_search($criteria, $only_keys, $order_by, $extra_cols, $wildcard, $empty, $op, $start, $filter, $join, $ignore_acl);
}
catch (\Exception $e) {
// try regular search
}
}
return parent::search($criteria, $only_keys, $order_by, $extra_cols, $wildcard, $empty, $op, $start, $filter, $join, $ignore_acl);
}
/**
* searches contacts containing a given phone-number
*
* @param string $criteria phone-number
* @param boolean|string $only_keys =true True returns only keys, False returns all cols. comma seperated list of keys to return
* @param string $order_by ='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY)
* @param string|array $extra_cols ='' string or array of strings to be added to the SELECT, eg. "count(*) as num"
* @param string $wildcard ='' appended befor and after each criteria
* @param boolean $empty =false False=empty criteria are ignored in query, True=empty have to be empty in row
* @param string $op ='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together
* @param mixed $start =false if != false, return only maxmatch rows begining with start, or array($start,$num)
* @param array $filter =null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards
* $filter['cols_to_search'] limit search columns to given columns, otherwise $this->columns_to_search is used
* @param string $join ='' sql to do a join (only used by sql backend!), eg. " RIGHT JOIN egw_accounts USING(account_id)"
* @param boolean $ignore_acl =false true: no acl check
* @return array of matching rows (the row is an array of the cols) or False
* @throws Exception\WrongParameter|\libphonenumber\NumberParseException if $critera is not a string with a valid phone-number
* @throws Exception\NotFound if no contact matches the phone-number in $criteria
*/
function &phone_search($criteria, $only_keys = True, $order_by = '', $extra_cols = '', $wildcard = '', $empty = False, $op = 'AND', $start = false, $filter = null, $join = '', $ignore_acl = false)
{
$phoneNumberUtil = PhoneNumberUtil::getInstance();
$region = $GLOBALS['egw_info']['user']['preferences']['common']['country'] ?: 'DE';
$number = $phoneNumberUtil->parse($criteria, $region);
if (!$phoneNumberUtil->isValidNumber($number))
{
throw new Exception\WrongParameter('Not a valid phone-number!');
}
if ($only_keys === true) $only_keys = false;
$start = false; // no pagination
// search for
list($country, $area, $rest) = explode(' ',
$phoneNumberUtil->format($number, \libphonenumber\PhoneNumberFormat::INTERNATIONAL), 3);
$rest_without_space = str_replace(' ', '', $rest);
foreach([
$area.' +'.$rest_without_space,
$area.' +'.substr($rest_without_space, 0, -3), // strip last 3 digits, in case they are written as extension
] as $pattern)
{
$rows = parent::search($pattern, $only_keys, $order_by, $extra_cols, $wildcard, $empty, $op, $start, $filter, $join, $ignore_acl) ?: [];
foreach($rows as $key => $row)
{
$found = false;
foreach($row as $name => $value)
{
if (substr($name, 0, 4) === 'tel_' && !empty($value))
{
try {
$tel = $phoneNumberUtil->parse($value, $region);
if (($found = $tel->equals($number))) break;
}
catch (\Exception $e) {
// ignore broken numbers
}
}
}
if (!$found) unset($rows[$key]);
}
if ($rows)
{
$this->total = count($rows);
return $rows;
}
}
throw new Exception\NotFound("No contacts with phone number '$criteria' found!");
}
}

View File

@ -75,6 +75,7 @@
"egroupware/adodb-php": "self.version",
"egroupware/bookmarks": "self.version",
"egroupware/collabora": "self.version",
"egroupware/guzzlestream": "dev-master",
"egroupware/icalendar": "^2.1.9",
"egroupware/magicsuggest": "^2.1",
"egroupware/news_admin": "self.version",
@ -86,10 +87,10 @@
"egroupware/status": "self.version",
"egroupware/swoolepush": "self.version",
"egroupware/tracker": "self.version",
"egroupware/webdav": "dev-master",
"egroupware/z-push-dev": "^2.5",
"fxp/composer-asset-plugin": "^1.2.2",
"egroupware/guzzlestream": "dev-master",
"egroupware/webdav": "dev-master",
"giggsey/libphonenumber-for-php": "^8.12",
"npm-asset/as-jqplot": "1.0.*",
"npm-asset/gridster": "0.5.*",
"oomphinc/composer-installers-extender": "^1.1",

478
composer.lock generated
View File

@ -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": "917b16b4edc00f7754459b39bf9441e2",
"content-hash": "5497944e56c87a0be09e444b46a36bbe",
"packages": [
{
"name": "adldap2/adldap2",
@ -1088,12 +1088,12 @@
{
"name": "Chuck Hagenbuch",
"email": "chuck@horde.org",
"role": "Lead"
"role": "lead"
},
{
"name": "Jan Schneider",
"email": "jan@horde.org",
"role": "Lead"
"role": "lead"
},
{
"name": "Michael J Rubinsky",
@ -1140,7 +1140,7 @@
],
"description": "Compiled version of magicsuggest customized for EGroupware project.",
"homepage": "https://github.com/EGroupware/magicsuggest",
"time": "2018-06-21T13:36:37+00:00"
"time": "2018-06-21T10:14:03+00:00"
},
{
"name": "egroupware/news_admin",
@ -1832,6 +1832,123 @@
],
"time": "2019-11-13T10:30:21+00:00"
},
{
"name": "giggsey/libphonenumber-for-php",
"version": "8.12.18",
"source": {
"type": "git",
"url": "https://github.com/giggsey/libphonenumber-for-php.git",
"reference": "2d50e0aea1f70508b39a3d22c62d24d66d532ed2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/2d50e0aea1f70508b39a3d22c62d24d66d532ed2",
"reference": "2d50e0aea1f70508b39a3d22c62d24d66d532ed2",
"shasum": ""
},
"require": {
"giggsey/locale": "^1.7",
"php": ">=5.3.2",
"symfony/polyfill-mbstring": "^1.17"
},
"require-dev": {
"pear/pear-core-minimal": "^1.9",
"pear/pear_exception": "^1.0",
"pear/versioncontrol_git": "^0.5",
"phing/phing": "^2.7",
"php-coveralls/php-coveralls": "^1.0|^2.0",
"symfony/console": "^2.8|^3.0",
"symfony/phpunit-bridge": "^4.2 || ^5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "8.x-dev"
}
},
"autoload": {
"psr-4": {
"libphonenumber\\": "src/"
},
"exclude-from-classmap": [
"/src/data/",
"/src/carrier/data/",
"/src/geocoding/data/",
"/src/timezone/data/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Joshua Gigg",
"email": "giggsey@gmail.com",
"homepage": "https://giggsey.com/"
}
],
"description": "PHP Port of Google's libphonenumber",
"homepage": "https://github.com/giggsey/libphonenumber-for-php",
"keywords": [
"geocoding",
"geolocation",
"libphonenumber",
"mobile",
"phonenumber",
"validation"
],
"time": "2021-02-08T10:31:32+00:00"
},
{
"name": "giggsey/locale",
"version": "1.9",
"source": {
"type": "git",
"url": "https://github.com/giggsey/Locale.git",
"reference": "b07f1eace8072ccc61445ad8fbd493ff9d783043"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/giggsey/Locale/zipball/b07f1eace8072ccc61445ad8fbd493ff9d783043",
"reference": "b07f1eace8072ccc61445ad8fbd493ff9d783043",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"pear/pear-core-minimal": "^1.9",
"pear/pear_exception": "^1.0",
"pear/versioncontrol_git": "^0.5",
"phing/phing": "~2.7",
"php-coveralls/php-coveralls": "^1.0|^2.0",
"phpunit/phpunit": "^4.8|^5.0",
"symfony/console": "^2.8|^3.0|^4.0",
"symfony/filesystem": "^2.8|^3.0|^4.0",
"symfony/finder": "^2.8|^3.0|^4.0",
"symfony/process": "^2.8|^3.0|^4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Giggsey\\Locale\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Joshua Gigg",
"email": "giggsey@gmail.com",
"homepage": "http://giggsey.com/"
}
],
"description": "Locale functions required by libphonenumber-for-php",
"time": "2020-07-07T11:16:24+00:00"
},
{
"name": "guzzle/common",
"version": "v3.9.2",
@ -2657,7 +2774,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library to wrap various compression techniques."
"description": "An API for various compression techniques."
},
{
"name": "pear-pear.horde.org/Horde_Crypt",
@ -2723,7 +2840,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides blowfish encryption/decryption for PHP string data."
"description": "Provides blowfish encryption/decryption for PHP string data."
},
{
"name": "pear-pear.horde.org/Horde_Date",
@ -2839,7 +2956,7 @@
"license": [
"BSD-2-Clause"
],
"description": "A library that wraps various backends providing IDNA (Internationalized Domain Names in Applications) support."
"description": "Normalized access to various backends providing IDNA (Internationalized Domain Names in Applications) support."
},
{
"name": "pear-pear.horde.org/Horde_Imap_Client",
@ -2877,7 +2994,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library to access IMAP4rev1 (RFC 3501) mail servers. Also supports connections to POP3 (STD 53/RFC 1939)."
"description": "Interface to access IMAP4rev1 (RFC 3501) mail servers. Also supports connections to POP3 (STD 53/RFC 1939)."
},
{
"name": "pear-pear.horde.org/Horde_ListHeaders",
@ -3036,7 +3153,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides methods for dealing with Multipurpose Internet Mail Extensions (MIME) features (RFC 2045/2046/2047)."
"description": "Provides methods for dealing with Multipurpose Internet Mail Extensions (MIME) features (RFC 2045/2046/2047)."
},
{
"name": "pear-pear.horde.org/Horde_Nls",
@ -3129,7 +3246,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library for connecting to a SMTP (RFC 5321) server to send e-mail messages."
"description": "Provides interfaces for connecting to a SMTP (RFC 5321) server to send e-mail messages."
},
{
"name": "pear-pear.horde.org/Horde_Socket_Client",
@ -3157,7 +3274,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides an abstract PHP network socket client."
"description": "Provides abstract class for use in creating PHP network socket clients."
},
{
"name": "pear-pear.horde.org/Horde_Stream",
@ -3298,7 +3415,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides a text-based diff engine and renderers for multiple diff output formats."
"description": "A text-based diff engine and renderers for multiple diff output formats."
},
{
"name": "pear-pear.horde.org/Horde_Text_Flowed",
@ -3326,7 +3443,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides common methods for manipulating text using the encoding described in RFC 3676 ('flowed' text)."
"description": "The Horde_Text_Flowed:: class provides common methods for manipulating text using the encoding described in RFC 3676 ('flowed' text)."
},
{
"name": "pear-pear.horde.org/Horde_Translation",
@ -3409,7 +3526,7 @@
"license": [
"LGPL-2.1"
],
"description": "A library that provides functionality useful for all kind of applications."
"description": "These classes provide functionality useful for all kind of applications."
},
{
"name": "pear/archive_tar",
@ -3475,16 +3592,6 @@
"archive",
"tar"
],
"funding": [
{
"url": "https://github.com/mrook",
"type": "github"
},
{
"url": "https://www.patreon.com/michielrook",
"type": "patreon"
}
],
"time": "2021-01-18T19:32:54+00:00"
},
{
@ -4106,12 +4213,6 @@
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"funding": [
{
"url": "https://github.com/synchro",
"type": "github"
}
],
"time": "2020-05-27T12:24:03+00:00"
},
{
@ -4204,20 +4305,6 @@
"x.509",
"x509"
],
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2020-04-04T23:17:33+00:00"
},
{
@ -6432,20 +6519,6 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-27T16:54:36+00:00"
},
{
@ -6503,20 +6576,6 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-10T07:47:39+00:00"
},
{
@ -6590,20 +6649,6 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-30T10:09:30+00:00"
},
{
@ -6661,20 +6706,6 @@
],
"description": "Symfony ErrorHandler Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-17T09:56:45+00:00"
},
{
@ -6745,20 +6776,6 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-13T14:18:44+00:00"
},
{
@ -6821,20 +6838,6 @@
"interoperability",
"standards"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-06T13:19:58+00:00"
},
{
@ -6885,20 +6888,6 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-27T16:54:36+00:00"
},
{
@ -6954,20 +6943,6 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-17T07:39:58+00:00"
},
{
@ -7059,20 +7034,6 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-09-02T08:09:29+00:00"
},
{
@ -7135,20 +7096,6 @@
"mime",
"mime-type"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-17T09:56:45+00:00"
},
{
@ -7211,20 +7158,6 @@
"polyfill",
"portable"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7296,20 +7229,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-04T06:02:08+00:00"
},
{
@ -7377,20 +7296,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7454,20 +7359,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7587,20 +7478,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7660,20 +7537,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7736,20 +7599,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7816,20 +7665,6 @@
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
@ -7958,20 +7793,6 @@
"uri",
"url"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-30T11:41:10+00:00"
},
{
@ -8107,20 +7928,6 @@
"debug",
"dump"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-08-17T07:31:35+00:00"
},
{
@ -8180,20 +7987,6 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-30T11:41:10+00:00"
},
{
@ -8370,12 +8163,12 @@
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
"shasum": ""
},
@ -10226,6 +10019,7 @@
"egroupware/adodb-php": 20,
"egroupware/bookmarks": 20,
"egroupware/collabora": 20,
"egroupware/guzzlestream": 20,
"egroupware/news_admin": 20,
"egroupware/openid": 20,
"egroupware/projectmanager": 20,
@ -10235,7 +10029,6 @@
"egroupware/status": 20,
"egroupware/swoolepush": 20,
"egroupware/tracker": 20,
"egroupware/guzzlestream": 20,
"egroupware/webdav": 20
},
"prefer-stable": true,
@ -10254,6 +10047,5 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.3"
},
"plugin-api-version": "1.1.0"
}
}