mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-25 09:23:28 +01:00
allow to use non-ascii chars for link-ids e.g. the name of Wiki pages can contain them
we store them as "\uXXXX" in the ascii column
This commit is contained in:
parent
bce12028c7
commit
6332470072
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* EGroupware API - Interapplicaton links storage
|
* EGroupware API - Inter-application links storage
|
||||||
*
|
*
|
||||||
* Links have two ends each pointing to an entry, each entry is a double:
|
* Links have two ends each pointing to an entry, each entry is a double:
|
||||||
* - app app-name or directory-name of an egw application, eg. 'infolog'
|
* - app app-name or directory-name of an egw application, eg. 'infolog'
|
||||||
@ -8,11 +8,10 @@
|
|||||||
*
|
*
|
||||||
* @link http://www.egroupware.org
|
* @link http://www.egroupware.org
|
||||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||||
* @copyright 2001-2016 by RalfBecker@outdoor-training.de
|
* @copyright 2001-2023 by RalfBecker@outdoor-training.de
|
||||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
* @package api
|
* @package api
|
||||||
* @subpackage link
|
* @subpackage link
|
||||||
* @version $Id$
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace EGroupware\Api\Link;
|
namespace EGroupware\Api\Link;
|
||||||
@ -20,11 +19,13 @@ namespace EGroupware\Api\Link;
|
|||||||
use EGroupware\Api;
|
use EGroupware\Api;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generalized linking between entries of eGroupware apps - SO layer
|
* generalized linking between entries of EGroupware apps - SO layer
|
||||||
*
|
*
|
||||||
* All vars passed to this class get correct escaped to prevent query insertion.
|
* All vars passed to this class get correct escaped to prevent query insertion.
|
||||||
*
|
*
|
||||||
* All methods are now static!
|
* All methods are now static!
|
||||||
|
*
|
||||||
|
* Non-ascii chars in link_id1/2 are now encoded as "\uXXXX", to be able to store them in ascii columns.
|
||||||
*/
|
*/
|
||||||
class Storage
|
class Storage
|
||||||
{
|
{
|
||||||
@ -48,7 +49,7 @@ class Storage
|
|||||||
public static $limit_exceeded = false;
|
public static $limit_exceeded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creats a link between $app1,$id1 and $app2,$id2
|
* creates a link between $app1,$id1 and $app2,$id2
|
||||||
*
|
*
|
||||||
* @param string $app1 appname of 1. endpoint of the link
|
* @param string $app1 appname of 1. endpoint of the link
|
||||||
* @param string $id1 id in $app1
|
* @param string $id1 id in $app1
|
||||||
@ -57,7 +58,7 @@ class Storage
|
|||||||
* @param string $remark ='' Remark to be saved with the link (defaults to '')
|
* @param string $remark ='' Remark to be saved with the link (defaults to '')
|
||||||
* @param int $owner =0 Owner of the link (defaults to user)
|
* @param int $owner =0 Owner of the link (defaults to user)
|
||||||
* @param int $lastmod =0 timestamp of last modification (defaults to now=time())
|
* @param int $lastmod =0 timestamp of last modification (defaults to now=time())
|
||||||
* @return int/boolean False (for db or param-error) or on success link_id (Please not the return-value of $id1)
|
* @return int|boolean False (for db or param-error) or on success link_id (Please not the return-value of $id1)
|
||||||
*/
|
*/
|
||||||
static function link( $app1,&$id1,$app2,$id2='',$remark='',$owner=0,$lastmod=0 )
|
static function link( $app1,&$id1,$app2,$id2='',$remark='',$owner=0,$lastmod=0 )
|
||||||
{
|
{
|
||||||
@ -82,15 +83,15 @@ class Storage
|
|||||||
{
|
{
|
||||||
$owner = $GLOBALS['egw_info']['user']['account_id'];
|
$owner = $GLOBALS['egw_info']['user']['account_id'];
|
||||||
}
|
}
|
||||||
return self::$db->insert(self::TABLE,array(
|
return self::$db->insert(self::TABLE, self::encodeRow([
|
||||||
'link_app1' => $app1,
|
'link_app1' => $app1,
|
||||||
'link_id1' => $id1,
|
'link_id1' => $id1,
|
||||||
'link_app2' => $app2,
|
'link_app2' => $app2,
|
||||||
'link_id2' => $id2,
|
'link_id2' => $id2,
|
||||||
'link_remark' => $remark,
|
'link_remark' => $remark,
|
||||||
'link_lastmod' => $lastmod ? $lastmod : time(),
|
'link_lastmod' => $lastmod ?: time(),
|
||||||
'link_owner' => $owner,
|
'link_owner' => $owner,
|
||||||
),False,__LINE__,__FILE__) ? self::$db->get_last_insert_id(self::TABLE,'link_id') : false;
|
]),False,__LINE__,__FILE__) ? self::$db->get_last_insert_id(self::TABLE,'link_id') : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,16 +145,18 @@ class Storage
|
|||||||
|
|
||||||
$links = array();
|
$links = array();
|
||||||
try {
|
try {
|
||||||
foreach(self::$db->select(self::TABLE, '*', self::$db->expression(self::TABLE, '((', array(
|
foreach(self::$db->select(self::TABLE, '*', self::$db->expression(self::TABLE, '((', self::encodeRow([
|
||||||
'link_app1' => $app,
|
'link_app1' => $app,
|
||||||
'link_id1' => $id,
|
'link_id1' => $id,
|
||||||
),') OR (',array(
|
]), ') OR (', self::encodeRow([
|
||||||
'link_app2' => $app,
|
'link_app2' => $app,
|
||||||
'link_id2' => $id,
|
'link_id2' => $id,
|
||||||
),'))',
|
]), '))',
|
||||||
$deleted ? '' : ' AND deleted IS NULL'
|
$deleted ? '' : ' AND deleted IS NULL'
|
||||||
), __LINE__, __FILE__, $offset, $order ? " ORDER BY $order" : '', 'phpgwapi', $limit) as $row)
|
), __LINE__, __FILE__, $offset, $order ? " ORDER BY $order" : '', 'phpgwapi', $limit) as $row)
|
||||||
{
|
{
|
||||||
|
$row = self::decodeRow($row);
|
||||||
|
|
||||||
// check if left side (1) is one of our targets --> add it
|
// check if left side (1) is one of our targets --> add it
|
||||||
if ($row['link_app1'] == $app && in_array($row['link_id1'],(array)$id))
|
if ($row['link_app1'] == $app && in_array($row['link_id1'],(array)$id))
|
||||||
{
|
{
|
||||||
@ -224,20 +227,25 @@ class Storage
|
|||||||
{
|
{
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
$where = self::$db->expression(self::TABLE,'(',array(
|
$where = self::$db->expression(self::TABLE,'(',self::encodeRow([
|
||||||
'link_app1' => $app_link_id,
|
'link_app1' => $app_link_id,
|
||||||
'link_id1' => $id,
|
'link_id1' => $id,
|
||||||
'link_app2' => $app2,
|
'link_app2' => $app2,
|
||||||
'link_id2' => $id2,
|
'link_id2' => $id2,
|
||||||
),') OR (',array(
|
]),') OR (',self::encodeRow([
|
||||||
'link_app2' => $app_link_id,
|
'link_app2' => $app_link_id,
|
||||||
'link_id2' => $id,
|
'link_id2' => $id,
|
||||||
'link_app1' => $app2,
|
'link_app1' => $app2,
|
||||||
'link_id1' => $id2,
|
'link_id1' => $id2,
|
||||||
),')');
|
]),')');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return self::$db->select(self::TABLE,'*',$where,__LINE__,__FILE__)->fetch(ADODB_FETCH_ASSOC);
|
$row = self::$db->select(self::TABLE,'*',$where,__LINE__,__FILE__)->fetch(ADODB_FETCH_ASSOC);
|
||||||
|
if (is_array($row))
|
||||||
|
{
|
||||||
|
$row = self::decodeRow($row);
|
||||||
|
}
|
||||||
|
return $row;
|
||||||
}
|
}
|
||||||
// catch Illegal mix of collations (ascii_general_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=' (1267)
|
// catch Illegal mix of collations (ascii_general_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=' (1267)
|
||||||
// caused by non-ascii chars compared with ascii field uid
|
// caused by non-ascii chars compared with ascii field uid
|
||||||
@ -284,21 +292,21 @@ class Storage
|
|||||||
$check1['link_id1'] = $id;
|
$check1['link_id1'] = $id;
|
||||||
$check2['link_id2'] = $id;
|
$check2['link_id2'] = $id;
|
||||||
}
|
}
|
||||||
$where = self::$db->expression(self::TABLE,'((',$check1,') OR (',$check2,'))');
|
$where = self::$db->expression(self::TABLE,'((', self::encodeRow($check1), ') OR (', self::encodeRow($check2), '))');
|
||||||
}
|
}
|
||||||
elseif ($app != '' && $app2 != '')
|
elseif ($app != '' && $app2 != '')
|
||||||
{
|
{
|
||||||
$where = self::$db->expression(self::TABLE,'(',array(
|
$where = self::$db->expression(self::TABLE,'(', self::encodeRow([
|
||||||
'link_app1' => $app,
|
'link_app1' => $app,
|
||||||
'link_id1' => $id,
|
'link_id1' => $id,
|
||||||
'link_app2' => $app2,
|
'link_app2' => $app2,
|
||||||
'link_id2' => $id2,
|
'link_id2' => $id2,
|
||||||
),') OR (',array(
|
]), ') OR (', self::encodeRow([
|
||||||
'link_app1' => $app2,
|
'link_app1' => $app2,
|
||||||
'link_id1' => $id2,
|
'link_id1' => $id2,
|
||||||
'link_app2' => $app,
|
'link_app2' => $app,
|
||||||
'link_id2' => $id,
|
'link_id2' => $id,
|
||||||
),')');
|
]),')');
|
||||||
}
|
}
|
||||||
if ($owner)
|
if ($owner)
|
||||||
{
|
{
|
||||||
@ -310,7 +318,7 @@ class Storage
|
|||||||
try {
|
try {
|
||||||
foreach(self::$db->select(self::TABLE,'*',$where,__LINE__,__FILE__) as $row)
|
foreach(self::$db->select(self::TABLE,'*',$where,__LINE__,__FILE__) as $row)
|
||||||
{
|
{
|
||||||
$deleted[] = $row;
|
$deleted[] = self::decodeRow($row);
|
||||||
}
|
}
|
||||||
if($hold_for_purge)
|
if($hold_for_purge)
|
||||||
{
|
{
|
||||||
@ -357,7 +365,7 @@ class Storage
|
|||||||
$check1['link_id1'] = $id;
|
$check1['link_id1'] = $id;
|
||||||
$check2['link_id2'] = $id;
|
$check2['link_id2'] = $id;
|
||||||
}
|
}
|
||||||
$where = self::$db->expression(self::TABLE,'((',$check1,') OR (',$check2,'))');
|
$where = self::$db->expression(self::TABLE,'((', self::encodeRow($check1), ') OR (', self::encodeRow($check2), '))');
|
||||||
self::$db->update(self::TABLE,array('deleted'=> null), $where, __LINE__,__FILE__);
|
self::$db->update(self::TABLE,array('deleted'=> null), $where, __LINE__,__FILE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,7 +420,7 @@ class Storage
|
|||||||
array('table'=>self::TABLE,
|
array('table'=>self::TABLE,
|
||||||
'cols'=>'c.*,b.link_app1 AS app3,b.link_id1 AS id3,b.link_id AS link3',
|
'cols'=>'c.*,b.link_app1 AS app3,b.link_id1 AS id3,b.link_id AS link3',
|
||||||
'where'=>'a.link_app1='.self::$db->quote($app).' AND c.link_app2='.self::$db->quote($target_app).
|
'where'=>'a.link_app1='.self::$db->quote($app).' AND c.link_app2='.self::$db->quote($target_app).
|
||||||
(!$target_id ? '' : self::$db->expression(self::TABLE,' AND c.',array('link_id2' => $target_id))),
|
(!$target_id ? '' : self::$db->expression(self::TABLE, ' AND c.', self::encodeRow(['link_id2' => $target_id]))),
|
||||||
'join'=>" a
|
'join'=>" a
|
||||||
JOIN $table b ON a.link_id2=b.link_id1 AND a.link_app2=b.link_app1
|
JOIN $table b ON a.link_id2=b.link_id1 AND a.link_app2=b.link_app1
|
||||||
JOIN $table c ON a.link_id1=c.link_id1 AND a.link_app1=c.link_app1 AND a.link_id!=c.link_id AND c.link_app2=b.link_app2 AND c.link_id2=b.link_id2",
|
JOIN $table c ON a.link_id1=c.link_id1 AND a.link_app1=c.link_app1 AND a.link_id!=c.link_id AND c.link_app2=b.link_app2 AND c.link_id2=b.link_id2",
|
||||||
@ -421,7 +429,7 @@ class Storage
|
|||||||
array('table'=>self::TABLE,
|
array('table'=>self::TABLE,
|
||||||
'cols'=>'b.link_id, b.link_app2 as app1, b.link_id2 as id1, b.link_app1 as app2, b.link_id1 as id2, b.link_remark,b.link_lastmod,b.link_owner,b.deleted,c.link_app1 AS app3,c.link_id1 AS id3,c.link_id AS link3',
|
'cols'=>'b.link_id, b.link_app2 as app1, b.link_id2 as id1, b.link_app1 as app2, b.link_id1 as id2, b.link_remark,b.link_lastmod,b.link_owner,b.deleted,c.link_app1 AS app3,c.link_id1 AS id3,c.link_id AS link3',
|
||||||
'where'=>'a.link_app1='.self::$db->quote($app).' AND b.link_app1='.self::$db->quote($target_app).
|
'where'=>'a.link_app1='.self::$db->quote($app).' AND b.link_app1='.self::$db->quote($target_app).
|
||||||
(!$target_id ? '' : self::$db->expression(self::TABLE,' AND b.',array('link_id1' => $target_id))),
|
(!$target_id ? '' : self::$db->expression(self::TABLE, ' AND b.', self::encodeRow(['link_id1' => $target_id]))),
|
||||||
'join'=>" a
|
'join'=>" a
|
||||||
JOIN $table b ON a.link_id1=b.link_id2 AND a.link_app1=b.link_app2
|
JOIN $table b ON a.link_id1=b.link_id2 AND a.link_app1=b.link_app2
|
||||||
JOIN $table c ON a.link_id2=c.link_id1 AND a.link_app2=c.link_app1 AND a.link_id!=c.link_id AND c.link_app2=b.link_app1 AND c.link_id2=b.link_id1",
|
JOIN $table c ON a.link_id2=c.link_id1 AND a.link_app2=c.link_app1 AND a.link_id!=c.link_id AND c.link_app2=b.link_app1 AND c.link_id2=b.link_id1",
|
||||||
@ -430,7 +438,7 @@ class Storage
|
|||||||
array('table'=>self::TABLE,
|
array('table'=>self::TABLE,
|
||||||
'cols'=>'a.*,c.link_app1 AS app3,c.link_id1 AS id3,c.link_id AS link3',
|
'cols'=>'a.*,c.link_app1 AS app3,c.link_id1 AS id3,c.link_id AS link3',
|
||||||
'where'=>'a.link_app1='.self::$db->quote($app).' AND a.link_app2='.self::$db->quote($target_app).
|
'where'=>'a.link_app1='.self::$db->quote($app).' AND a.link_app2='.self::$db->quote($target_app).
|
||||||
(!$target_id ? '' : self::$db->expression(self::TABLE,' AND a.',array('link_id2' => $target_id))),
|
(!$target_id ? '' : self::$db->expression(self::TABLE,' AND a.', self::encodeRow(['link_id2' => $target_id]))),
|
||||||
'join'=>" a
|
'join'=>" a
|
||||||
JOIN $table b ON a.link_id1=b.link_id2 AND a.link_app1=b.link_app2
|
JOIN $table b ON a.link_id1=b.link_id2 AND a.link_app1=b.link_app2
|
||||||
JOIN $table c ON a.link_id2=c.link_id2 AND a.link_app2=c.link_app2 AND a.link_id!=c.link_id AND c.link_app1=b.link_app1 AND c.link_id1=b.link_id1",
|
JOIN $table c ON a.link_id2=c.link_id2 AND a.link_app2=c.link_app2 AND a.link_id!=c.link_id AND c.link_app1=b.link_app1 AND c.link_id1=b.link_id1",
|
||||||
@ -450,6 +458,7 @@ class Storage
|
|||||||
$links = array();
|
$links = array();
|
||||||
foreach(self::$db->union($arrayofselects, __LINE__, __FILE__, $order, $offset, $limit) as $row)
|
foreach(self::$db->union($arrayofselects, __LINE__, __FILE__, $order, $offset, $limit) as $row)
|
||||||
{
|
{
|
||||||
|
$row = self::decodeRow($row);
|
||||||
if ($just_app_ids)
|
if ($just_app_ids)
|
||||||
{
|
{
|
||||||
if ($row['link_app1'] == $target_app && (is_null($target_id) || in_array($row['link_id1'],(array)$target_id)))
|
if ($row['link_app1'] == $target_app && (is_null($target_id) || in_array($row['link_id1'],(array)$target_id)))
|
||||||
@ -472,6 +481,70 @@ class Storage
|
|||||||
return $links;
|
return $links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode id to be stored in ascii column by encoding non-ascii utf8 chars as \uXXXX
|
||||||
|
*
|
||||||
|
* @param string|int|array $id one or multiple ids
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
private static function encodeId($id)
|
||||||
|
{
|
||||||
|
if (is_array($id))
|
||||||
|
{
|
||||||
|
return array_map([__CLASS__, __METHOD__], $id);
|
||||||
|
}
|
||||||
|
return substr(json_encode($id, JSON_UNESCAPED_SLASHES), 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoding row with link_id1/2 stored in ascii column by encoding non-ascii utf8 chars as \uXXXX
|
||||||
|
*
|
||||||
|
* @param array $row
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private static function encodeRow(array $row)
|
||||||
|
{
|
||||||
|
if (isset($row['link_id1']))
|
||||||
|
{
|
||||||
|
$row['link_id1'] = self::encodeId($row['link_id1']);
|
||||||
|
}
|
||||||
|
if (isset($row['link_id2']))
|
||||||
|
{
|
||||||
|
$row['link_id2'] = self::encodeId($row['link_id2']);
|
||||||
|
}
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoding id stored in ascii column by decoding non-ascii utf8 chars stored as \uXXXX
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function decodeId(string $id)
|
||||||
|
{
|
||||||
|
return json_decode('"'.$id.'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decoding row with link_id1/2 stored in ascii column by decoding non-ascii utf8 chars stored as \uXXXX
|
||||||
|
*
|
||||||
|
* @param array $row
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private static function decodeRow(array $row)
|
||||||
|
{
|
||||||
|
if (isset($row['link_id1']))
|
||||||
|
{
|
||||||
|
$row['link_id1'] = self::decodeId($row['link_id1']);
|
||||||
|
}
|
||||||
|
if (isset($row['link_id2']))
|
||||||
|
{
|
||||||
|
$row['link_id2'] = self::decodeId($row['link_id2']);
|
||||||
|
}
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise our static vars
|
* Initialise our static vars
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user