Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F6066982
rcube_addressbook.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
rcube_addressbook.php
View Options
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Interface to the local address book database |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Abstract skeleton of an address book/repository
*
* @package Framework
* @subpackage Addressbook
*/
abstract
class
rcube_addressbook
{
// constants for error reporting
const
ERROR_READ_ONLY
=
1
;
const
ERROR_NO_CONNECTION
=
2
;
const
ERROR_VALIDATE
=
3
;
const
ERROR_SAVING
=
4
;
const
ERROR_SEARCH
=
5
;
// search modes
const
SEARCH_ALL
=
0
;
const
SEARCH_STRICT
=
1
;
const
SEARCH_PREFIX
=
2
;
const
SEARCH_GROUPS
=
4
;
// contact types, note: some of these are used as addressbook source identifiers
const
TYPE_CONTACT
=
0
;
const
TYPE_RECIPIENT
=
1
;
const
TYPE_TRUSTED_SENDER
=
2
;
const
TYPE_DEFAULT
=
4
;
const
TYPE_WRITEABLE
=
8
;
const
TYPE_READONLY
=
16
;
// public properties (mandatory)
/** @var string Name of the primary key field of this addressbook. Used to search for previously retrieved IDs. */
public
$primary_key
;
/** @var bool True if the addressbook supports contact groups. */
public
$groups
=
false
;
/**
* @var bool True if the addressbook supports exporting contact groups. Requires the implementation of
* get_record_groups().
*/
public
$export_groups
=
true
;
/** @var bool True if the addressbook is read-only. */
public
$readonly
=
true
;
/**
* @var bool True if the addressbook does not support listing all records but needs use of the search function.
*/
public
$searchonly
=
false
;
/** @var bool True if the addressbook supports restoring deleted contacts. */
public
$undelete
=
false
;
/** @var bool True if the addressbook is ready to be used. See rcmail_action_contacts_index::$CONTACT_COLTYPES */
public
$ready
=
false
;
/**
* @var null|string|int If set, addressbook-specific identifier of the selected group. All contact listing and
* contact searches will be limited to contacts that belong to this group.
*/
public
$group_id
=
null
;
/** @var int The current page of the listing. Numbering starts at 1. */
public
$list_page
=
1
;
/** @var int The maximum number of records shown on a page. */
public
$page_size
=
10
;
/** @var string Contact field by which to order listed records. */
public
$sort_col
=
'name'
;
/** @var string Whether sorting of records by $sort_col is done in ascending (ASC) or descending (DESC) order. */
public
$sort_order
=
'ASC'
;
/** @var string[] A list of record fields that contain dates. */
public
$date_cols
=
[];
/** @var array Definition of the contact fields supported by the addressbook. */
public
$coltypes
=
[
'name'
=>
[
'limit'
=>
1
],
'firstname'
=>
[
'limit'
=>
1
],
'surname'
=>
[
'limit'
=>
1
],
'email'
=>
[
'limit'
=>
1
]
];
/**
* @var string[] vCard additional fields mapping
*/
public
$vcard_map
=
[];
/** @var ?array Error state - hash array with the following fields: type, message */
protected
$error
;
/**
* Returns addressbook name (e.g. for addressbooks listing)
* @return string
*/
abstract
function
get_name
();
/**
* Sets a search filter.
*
* This affects the contact set considered when using the count() and list_records() operations to those
* contacts that match the filter conditions. If no search filter is set, all contacts in the addressbook are
* considered.
*
* This filter mechanism is applied in addition to other filter mechanisms, see the description of the count()
* operation.
*
* @param mixed $filter Search params to use in listing method, obtained by get_search_set()
* @return void
*/
abstract
function
set_search_set
(
$filter
);
/**
* Getter for saved search properties.
*
* The filter representation is opaque to roundcube, but can be set again using set_search_set().
*
* @return mixed Search properties used by this class
*/
abstract
function
get_search_set
();
/**
* Reset saved results and search parameters
* @return void
*/
abstract
function
reset
();
/**
* Refresh saved search set after data has changed
*
* @return mixed New search set
*/
function
refresh_search
()
{
return
$this
->
get_search_set
();
}
/**
* Lists the current set of contact records.
*
* See the description of count() for the criteria determining which contacts are considered for the listing.
*
* The actual records returned may be fewer, as only the records for the current page are returned. The returned
* records may be further limited by the $subset parameter, which means that only the first or last $subset records
* of the page are returned, depending on whether $subset is positive or negative. If $subset is 0, all records of
* the page are returned. The returned records are found in the $records property of the returned result set.
*
* Finally, the $first property of the returned result set contains the index into the total set of filtered records
* (i.e. not considering the segmentation into pages) of the first returned record before applying the $subset
* parameter (i.e., $first is always a multiple of the page size).
*
* The $nocount parameter is an optimization that allows to skip querying the total amount of records of the
* filtered set if the caller is only interested in the records. In this case, the $count property of the returned
* result set will simply contain the number of returned records, but the filtered set may contain more records than
* this.
*
* The result of the operation is internally cached for later retrieval using get_result().
*
* @param ?array $cols List of columns to include in the returned records (null means all)
* @param int $subset Only return this number of records of the current page, use negative values for tail
* @param bool $nocount True to skip the count query (select only)
*
* @return rcube_result_set Indexed list of contact records, each a hash array
*/
abstract
function
list_records
(
$cols
=
null
,
$subset
=
0
,
$nocount
=
false
);
/**
* Search records
*
* Depending on the given parameters the search() function operates in different ways (in the order listed):
*
* "Direct ID search" - when $fields is either 'ID' or $this->primary_key
* - $values is either a string of contact IDs separated by self::SEPARATOR (,) or an array of contact IDs
* - Any contact with one of the given IDs is returned
*
* "Advanced search" - when $value is an array
* - Each value in $values is the search value for the field in $fields at the same index
* - All fields must match their value to be included in the result ("AND" semantics)
*
* "Search all fields" - when $fields is '*' (note: $value is a single string)
* - Any field must match the value to be included in the result ("OR" semantics)
*
* "Search given fields" - if none of the above matches
* - Any of the given fields must match the value to be included in the result ("OR" semantics)
*
* All matching is done case insensitive.
*
* The search settings are remembered (see set_search_set()) until reset using the reset() function. They can be
* retrieved using get_search_set(). The remembered search settings must be considered by list_records() and
* count().
*
* The search mode can be set by the admin via the config.inc.php setting addressbook_search_mode.
* It is used as a bit mask, but the search modes are exclusive (SEARCH_GROUPS is combined with one of other modes):
* SEARCH_ALL: substring search (*abc*)
* SEARCH_STRICT: Exact match search (case insensitive =)
* SEARCH_PREFIX: Prefix search (abc*)
* SEARCH_GROUPS: include groups in search results (if supported)
*
* When records are requested in the returned rcube_result_set ($select is true), the results will only include the
* contacts of the current page (see list_page, page_size). The behavior is as described with the list_records
* function, and search() can be thought of as a sequence of set_search_set() and list_records() under that filter.
*
* If $nocount is true, the count property of the returned rcube_result_set will contain the amount of records
* contained within that set. Calling search() with $select=false and $nocount=true is not a meaningful use case and
* will result in an empty result set without records and a count property of 0, which gives no indication on the
* actual record set matching the given filter.
*
* The result of the operation is internally cached for later retrieval using get_result().
*
* @param string|string[] $fields Field names to search in
* @param string|string[] $value Search value, or array of values, one for each field in $fields
* @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*.
* @param bool $select True if records are requested in the result, false if count only
* @param bool $nocount True to skip the count query (select only)
* @param string|string[] $required Field or list of fields that cannot be empty
*
* @return rcube_result_set Contact records and 'count' value
*/
abstract
function
search
(
$fields
,
$value
,
$mode
=
0
,
$select
=
true
,
$nocount
=
false
,
$required
=
[]);
/**
* Count the number of contacts in the database matching the current filter criteria.
*
* The current filter criteria are defined by the search filter (see search()/set_search_set()) and the currently
* active group (see set_group()), if applicable.
*
* @return rcube_result_set Result set with values for 'count' and 'first'
*/
abstract
function
count
();
/**
* Return the last result set
*
* @return ?rcube_result_set Current result set or NULL if nothing selected yet
*/
abstract
function
get_result
();
/**
* Get a specific contact record
*
* @param mixed $id Record identifier(s)
* @param bool $assoc True to return record as associative array, otherwise a result set is returned
*
* @return rcube_result_set|array Result object with all record fields
*/
abstract
function
get_record
(
$id
,
$assoc
=
false
);
/**
* Returns the last error occurred (e.g. when updating/inserting failed)
*
* @return ?array Hash array with the following fields: type, message. Null if no error set.
*/
function
get_error
()
{
return
$this
->
error
;
}
/**
* Setter for errors for internal use
*
* @param int $type Error type (one of this class' error constants)
* @param string $message Error message (name of a text label)
*/
protected
function
set_error
(
$type
,
$message
)
{
$this
->
error
=
[
'type'
=>
$type
,
'message'
=>
$message
];
}
/**
* Close connection to source
* Called on script shutdown
*/
function
close
()
{
}
/**
* Set internal list page
*
* @param int $page Page number to list
*/
function
set_page
(
$page
)
{
$this
->
list_page
=
(
int
)
$page
;
}
/**
* Set internal page size
*
* @param int $size Number of messages to display on one page
*/
function
set_pagesize
(
$size
)
{
$this
->
page_size
=
(
int
)
$size
;
}
/**
* Set internal sort settings
*
* @param ?string $sort_col Sort column
* @param ?string $sort_order Sort order
*/
function
set_sort_order
(
$sort_col
,
$sort_order
=
null
)
{
if
(
$sort_col
&&
(
array_key_exists
(
$sort_col
,
$this
->
coltypes
)
||
in_array
(
$sort_col
,
$this
->
coltypes
)))
{
$this
->
sort_col
=
$sort_col
;
}
if
(
$sort_order
)
{
$this
->
sort_order
=
strtoupper
(
$sort_order
)
==
'DESC'
?
'DESC'
:
'ASC'
;
}
}
/**
* Check the given data before saving.
* If input isn't valid, the message to display can be fetched using get_error()
*
* @param array &$save_data Associative array with data to save
* @param bool $autofix Attempt to fix/complete record automatically
*
* @return bool True if input is valid, False if not.
*/
public
function
validate
(&
$save_data
,
$autofix
=
false
)
{
$rcube
=
rcube
::
get_instance
();
$valid
=
true
;
// check validity of email addresses
foreach
(
$this
->
get_col_values
(
'email'
,
$save_data
,
true
)
as
$email
)
{
if
(
strlen
(
$email
))
{
if
(!
rcube_utils
::
check_email
(
rcube_utils
::
idn_to_ascii
(
$email
)))
{
$error
=
$rcube
->
gettext
([
'name'
=>
'emailformaterror'
,
'vars'
=>
[
'email'
=>
$email
]]);
$this
->
set_error
(
self
::
ERROR_VALIDATE
,
$error
);
$valid
=
false
;
break
;
}
}
}
// allow plugins to do contact validation and auto-fixing
$plugin
=
$rcube
->
plugins
->
exec_hook
(
'contact_validate'
,
[
'record'
=>
$save_data
,
'autofix'
=>
$autofix
,
'valid'
=>
$valid
,
]);
if
(
$valid
&&
!
$plugin
[
'valid'
])
{
$this
->
set_error
(
self
::
ERROR_VALIDATE
,
$plugin
[
'error'
]);
}
if
(
is_array
(
$plugin
[
'record'
]))
{
$save_data
=
$plugin
[
'record'
];
}
return
$plugin
[
'valid'
];
}
/**
* Create a new contact record
*
* @param array $save_data Associative array with save data
* Keys: Field name with optional section in the form FIELD:SECTION
* Values: Field value. Can be either a string or an array of strings for multiple values
* @param bool $check True to check for duplicates first
*
* @return mixed The created record ID on success, False on error
*/
function
insert
(
$save_data
,
$check
=
false
)
{
// empty for read-only address books
}
/**
* Create new contact records for every item in the record set
*
* @param rcube_result_set $recset Recordset to insert
* @param bool $check True to check for duplicates first
*
* @return array List of created record IDs
*/
function
insertMultiple
(
$recset
,
$check
=
false
)
{
$ids
=
[];
if
(
$recset
instanceof
rcube_result_set
)
{
while
(
$row
=
$recset
->
next
())
{
if
(
$insert
=
$this
->
insert
(
$row
,
$check
))
{
$ids
[]
=
$insert
;
}
}
}
return
$ids
;
}
/**
* Update a specific contact record
*
* @param mixed $id Record identifier
* @param array $save_cols Associative array with save data
* Keys: Field name with optional section in the form FIELD:SECTION
* Values: Field value. Can be either a string or an array of strings for multiple values
*
* @return mixed On success if ID has been changed returns ID, otherwise True, False on error
*/
function
update
(
$id
,
$save_cols
)
{
// empty for read-only address books
}
/**
* Mark one or more contact records as deleted
*
* @param array $ids Record identifiers
* @param bool $force Remove records irreversible (see self::undelete)
*
* @return int|false Number of removed records, False on failure
*/
function
delete
(
$ids
,
$force
=
true
)
{
// empty for read-only address books
}
/**
* Unmark delete flag on contact record(s)
*
* @param array $ids Record identifiers
*/
function
undelete
(
$ids
)
{
// empty for read-only address books
}
/**
* Mark all records in database as deleted
*
* @param bool $with_groups Remove also groups
*/
function
delete_all
(
$with_groups
=
false
)
{
// empty for read-only address books
}
/**
* Sets/clears the current group.
*
* This affects the contact set considered when using the count(), list_records() and search() operations to those
* contacts that belong to the given group. If no current group is set, all contacts in the addressbook are
* considered.
*
* This filter mechanism is applied in addition to other filter mechanisms, see the description of the count()
* operation.
*
* @param null|0|string $gid Database identifier of the group. 0/"0"/null to reset the group filter.
*/
function
set_group
(
$group_id
)
{
// empty for address books don't supporting groups
}
/**
* List all active contact groups of this source
*
* @param ?string $search Optional search string to match group name
* @param int $mode Search mode. Sum of self::SEARCH_*
*
* @return array Indexed list of contact groups, each a hash array
*/
function
list_groups
(
$search
=
null
,
$mode
=
0
)
{
// empty for address books don't supporting groups
return
[];
}
/**
* Get group properties such as name and email address(es)
*
* @param string $group_id Group identifier
*
* @return ?array Group properties as hash array, null in case of error.
*/
function
get_group
(
$group_id
)
{
// empty for address books don't supporting groups
return
null
;
}
/**
* Create a contact group with the given name
*
* @param string $name The group name
*
* @return array|false False on error, array with record props in success
*/
function
create_group
(
$name
)
{
// empty for address books don't supporting groups
return
false
;
}
/**
* Delete the given group and all linked group members
*
* @param string $group_id Group identifier
*
* @return bool True on success, false if no data was changed
*/
function
delete_group
(
$group_id
)
{
// empty for address books don't supporting groups
return
false
;
}
/**
* Rename a specific contact group
*
* @param string $group_id Group identifier
* @param string $newname New name to set for this group
* @param string &$newid New group identifier (if changed, otherwise don't set)
*
* @return string|false New name on success, false if no data was changed
*/
function
rename_group
(
$group_id
,
$newname
,
&
$newid
)
{
// empty for address books don't supporting groups
return
false
;
}
/**
* Add the given contact records the a certain group
*
* @param string $group_id Group identifier
* @param array|string $ids List of contact identifiers to be added
*
* @return int Number of contacts added
*/
function
add_to_group
(
$group_id
,
$ids
)
{
// empty for address books don't supporting groups
return
0
;
}
/**
* Remove the given contact records from a certain group
*
* @param string $group_id Group identifier
* @param array|string $ids List of contact identifiers to be removed
*
* @return int Number of deleted group members
*/
function
remove_from_group
(
$group_id
,
$ids
)
{
// empty for address books don't supporting groups
return
0
;
}
/**
* Get group assignments of a specific contact record
*
* @param mixed $id Record identifier
*
* @return array List of assigned groups indexed by a group ID.
* Every array element can be just a group name (string), or an array
* with 'ID' and 'name' elements.
* @since 0.5-beta
*/
function
get_record_groups
(
$id
)
{
// empty for address books don't supporting groups
return
[];
}
/**
* Utility function to return all values of a certain data column
* either as flat list or grouped by subtype
*
* @param string $col Col name
* @param array $data Record data array as used for saving
* @param bool $flat True to return one array with all values,
* False for hash array with values grouped by type
*
* @return array List of column values
*/
public
static
function
get_col_values
(
$col
,
$data
,
$flat
=
false
)
{
$out
=
[];
foreach
((
array
)
$data
as
$c
=>
$values
)
{
if
(
$c
===
$col
||
strpos
(
$c
,
$col
.
':'
)
===
0
)
{
if
(
$flat
)
{
$out
=
array_merge
(
$out
,
(
array
)
$values
);
}
else
{
list
(,
$type
)
=
rcube_utils
::
explode
(
':'
,
$c
);
if
(
$type
!==
null
&&
isset
(
$out
[
$type
]))
{
$out
[
$type
]
=
array_merge
((
array
)
$out
[
$type
],
(
array
)
$values
);
}
else
{
$out
[
$type
]
=
(
array
)
$values
;
}
}
}
}
// remove duplicates
if
(
$flat
&&
!
empty
(
$out
))
{
$out
=
array_unique
(
$out
);
}
return
$out
;
}
/**
* Compose a valid display name from the given structured contact data
*
* @param array $contact Hash array with contact data as key-value pairs
* @param bool $full_email Don't attempt to extract components from the email address
*
* @return string Display name
*/
public
static
function
compose_display_name
(
$contact
,
$full_email
=
false
)
{
$contact
=
rcube
::
get_instance
()->
plugins
->
exec_hook
(
'contact_displayname'
,
$contact
);
$fn
=
isset
(
$contact
[
'name'
])
?
$contact
[
'name'
]
:
''
;
// default display name composition according to vcard standard
if
(!
$fn
)
{
$keys
=
[
'prefix'
,
'firstname'
,
'middlename'
,
'surname'
,
'suffix'
];
$fn
=
implode
(
' '
,
array_filter
(
array_intersect_key
(
$contact
,
array_flip
(
$keys
))));
$fn
=
trim
(
preg_replace
(
'/
\s
+/u'
,
' '
,
$fn
));
}
// use email address part for name
$email
=
self
::
get_col_values
(
'email'
,
$contact
,
true
);
$email
=
isset
(
$email
[
0
])
?
$email
[
0
]
:
null
;
if
(
$email
&&
(
empty
(
$fn
)
||
$fn
==
$email
))
{
// return full email
if
(
$full_email
)
{
return
$email
;
}
list
(
$emailname
)
=
explode
(
'@'
,
$email
);
if
(
preg_match
(
'/(.*)[
\.\-\_
](.*)/'
,
$emailname
,
$match
))
{
$fn
=
trim
(
ucfirst
(
$match
[
1
]).
' '
.
ucfirst
(
$match
[
2
]));
}
else
{
$fn
=
ucfirst
(
$emailname
);
}
}
return
$fn
;
}
/**
* Compose the name to display in the contacts list for the given contact record.
* This respects the settings parameter how to list contacts.
*
* @param array $contact Hash array with contact data as key-value pairs
*
* @return string List name
*/
public
static
function
compose_list_name
(
$contact
)
{
static
$compose_mode
;
if
(!
isset
(
$compose_mode
))
{
$compose_mode
=
rcube
::
get_instance
()->
config
->
get
(
'addressbook_name_listing'
,
0
);
}
switch
(
$compose_mode
)
{
case
3
:
$fn
=
implode
(
' '
,
[
$contact
[
'surname'
]
.
','
,
$contact
[
'firstname'
],
$contact
[
'middlename'
]]);
break
;
case
2
:
$keys
=
[
'surname'
,
'firstname'
,
'middlename'
];
$fn
=
implode
(
' '
,
array_filter
(
array_intersect_key
(
$contact
,
array_flip
(
$keys
))));
break
;
case
1
:
$keys
=
[
'firstname'
,
'middlename'
,
'surname'
];
$fn
=
implode
(
' '
,
array_filter
(
array_intersect_key
(
$contact
,
array_flip
(
$keys
))));
break
;
case
0
:
if
(!
empty
(
$contact
[
'name'
]))
{
$fn
=
$contact
[
'name'
];
}
else
{
$keys
=
[
'prefix'
,
'firstname'
,
'middlename'
,
'surname'
,
'suffix'
];
$fn
=
implode
(
' '
,
array_filter
(
array_intersect_key
(
$contact
,
array_flip
(
$keys
))));
}
break
;
default
:
$plugin
=
rcube
::
get_instance
()->
plugins
->
exec_hook
(
'contact_listname'
,
[
'contact'
=>
$contact
]);
$fn
=
$plugin
[
'fn'
];
}
$fn
=
trim
(
$fn
,
', '
);
$fn
=
preg_replace
(
'/
\s
+/u'
,
' '
,
$fn
);
// fallbacks...
if
(
$fn
===
''
)
{
// ... display name
if
(
$name
=
trim
(
$contact
[
'name'
]))
{
$fn
=
$name
;
}
// ... organization
else
if
(
isset
(
$contact
[
'organization'
])
&&
(
$org
=
trim
(
$contact
[
'organization'
])))
{
$fn
=
$org
;
}
// ... email address
else
if
((
$email
=
self
::
get_col_values
(
'email'
,
$contact
,
true
))
&&
!
empty
(
$email
))
{
$fn
=
$email
[
0
];
}
}
return
$fn
;
}
/**
* Build contact display name for autocomplete listing
*
* @param array $contact Hash array with contact data as key-value pairs
* @param string $email Optional email address
* @param string $name Optional name (self::compose_list_name() result)
* @param string $templ Optional template to use (defaults to the 'contact_search_name' config option)
*
* @return string Display name
*/
public
static
function
compose_search_name
(
$contact
,
$email
=
null
,
$name
=
null
,
$templ
=
null
)
{
static
$template
;
if
(
empty
(
$templ
)
&&
!
isset
(
$template
))
{
// cache this
$template
=
rcube
::
get_instance
()->
config
->
get
(
'contact_search_name'
);
if
(
empty
(
$template
))
{
$template
=
'{name} <{email}>'
;
}
}
$result
=
$templ
?:
$template
;
if
(
preg_match_all
(
'/
\{
[a-z]+
\}
/'
,
$result
,
$matches
))
{
foreach
(
$matches
[
0
]
as
$key
)
{
$key
=
trim
(
$key
,
'{}'
);
$value
=
''
;
switch
(
$key
)
{
case
'name'
:
$value
=
$name
?:
self
::
compose_list_name
(
$contact
);
// If name(s) are undefined compose_list_name() may return an email address
// here we prevent from returning the same name and email
if
(
$name
===
$email
&&
strpos
(
$result
,
'{email}'
)
!==
false
)
{
$value
=
''
;
}
break
;
case
'email'
:
$value
=
$email
;
break
;
}
if
(
empty
(
$value
))
{
$value
=
strpos
(
$key
,
':'
)
?
$contact
[
$key
]
:
self
::
get_col_values
(
$key
,
$contact
,
true
);
if
(
is_array
(
$value
)
&&
isset
(
$value
[
0
]))
{
$value
=
$value
[
0
];
}
}
if
(!
is_string
(
$value
))
{
$value
=
''
;
}
$result
=
str_replace
(
'{'
.
$key
.
'}'
,
$value
,
$result
);
}
}
$result
=
preg_replace
(
'/
\s
+/u'
,
' '
,
$result
);
$result
=
preg_replace
(
'/
\s
*(<>|
\(\)
|
\[\]
)/u'
,
''
,
$result
);
$result
=
trim
(
$result
,
'/ '
);
return
$result
;
}
/**
* Create a unique key for sorting contacts
*
* @param array $contact Contact record
* @param string $sort_col Sorting column name
*
* @return string Unique key
*/
public
static
function
compose_contact_key
(
$contact
,
$sort_col
)
{
$key
=
$contact
[
$sort_col
];
// add email to a key to not skip contacts with the same name (#1488375)
if
((
$email
=
self
::
get_col_values
(
'email'
,
$contact
,
true
))
&&
!
empty
(
$email
))
{
$key
.=
':'
.
implode
(
':'
,
(
array
)
$email
);
}
// Make the key really unique (as we e.g. support contacts with no email)
$key
.=
':'
.
$contact
[
'sourceid'
]
.
':'
.
$contact
[
'ID'
];
return
$key
;
}
/**
* Compare search value with contact data
*
* @param string $colname Data name
* @param string|array $value Data value
* @param string $search Search value
* @param int $mode Search mode
*
* @return bool Comparison result
*/
protected
function
compare_search_value
(
$colname
,
$value
,
$search
,
$mode
)
{
// The value is a date string, for date we'll
// use only strict comparison (mode = 1)
// @TODO: partial search, e.g. match only day and month
if
(
in_array
(
$colname
,
$this
->
date_cols
))
{
return
((
$value
=
rcube_utils
::
anytodatetime
(
$value
))
&&
(
$search
=
rcube_utils
::
anytodatetime
(
$search
))
&&
$value
->
format
(
'Ymd'
)
==
$search
->
format
(
'Ymd'
));
}
// Gender is a special value, must use strict comparison (#5757)
if
(
$colname
==
'gender'
)
{
$mode
=
self
::
SEARCH_STRICT
;
}
// composite field, e.g. address
foreach
((
array
)
$value
as
$val
)
{
$val
=
mb_strtolower
(
$val
);
if
(
$mode
&
self
::
SEARCH_STRICT
)
{
$got
=
(
$val
==
$search
);
}
else
if
(
$mode
&
self
::
SEARCH_PREFIX
)
{
$got
=
(
$search
==
substr
(
$val
,
0
,
strlen
(
$search
)));
}
else
{
$got
=
(
strpos
(
$val
,
$search
)
!==
false
);
}
if
(
$got
)
{
return
true
;
}
}
return
false
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Fri, May 22, 5:02 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
768094
Default Alt Text
rcube_addressbook.php (30 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment