move addresbook_bo to Api\Contacts, ldap to Api\Ldap, ldapserverinfo to Api\Ldap\ServerInfo, bo_tracking to Api\Storage\Tracking, historylog to Api\Storage\History, Api\Customfields to Api\Storage\Customfields

This commit is contained in:
Ralf Becker 2016-03-06 13:45:15 +00:00
parent fcca19cfcf
commit 7ada2354d3
23 changed files with 5858 additions and 4957 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,13 @@
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @copyright (c) 2007-15 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
use EGroupware\Api;
/**
* SiteMgr contact form for the addressbook
*
@ -75,7 +77,7 @@ class addressbook_contactform
elseif ($content['submitit'])
{
$submitted = true;
$contact = new addressbook_bo();
$contact = new Api\Contacts();
if ($content['owner']) // save the contact in the addressbook
{
$content['private'] = 0; // in case default_private is set
@ -90,9 +92,9 @@ class addressbook_contactform
// the anonymous user to have run rights for addressbook AND
// edit rights for the addressbook used to store the new entry,
// which is clearly not wanted securitywise
egw_vfs::$is_root = true;
Api\Vfs::$is_root = true;
egw_link::link('addressbook',$id,egw_link::VFS_APPNAME,$value,$name);
egw_vfs::$is_root = false;
Api\Vfs::$is_root = false;
}
}
@ -108,8 +110,7 @@ class addressbook_contactform
{
if ($content['email_contactform'])
{
require_once(EGW_INCLUDE_ROOT.'/addressbook/inc/class.addressbook_tracking.inc.php');
$tracking = new addressbook_tracking($contact);
$tracking = new Api\Contacts\Tracking($contact);
}
if ($tracking->do_notifications($contact->data2db($content),null))
{
@ -141,7 +142,7 @@ class addressbook_contactform
static $contact;
if (is_null($contact))
{
$contact = new addressbook_bo();
$contact = new Api\Contacts();
}
$content['show']['custom'.$custom] = true;
$content['customfield'][$custom] = $name;
@ -174,7 +175,7 @@ class addressbook_contactform
if ($name[0] == '#') // custom field
{
static $contact;
if (is_null($contact)) $contact = new addressbook_bo();
if (is_null($contact)) $contact = new Api\Contacts();
$content['show']['custom'.$custom] = true;
$content['customfield'][$custom] = $name;
$content['customlabel'][$custom] = $contact->customfields[substr($name,1)]['label'];

View File

@ -7,10 +7,12 @@
* @package addressbook
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-15 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
use EGroupware\Api;
/**
* EGroupware: GroupDAV access: addressbook handler
*
@ -953,7 +955,7 @@ class addressbook_groupdav extends groupdav_handler
if (is_null($non_deleted_tids))
{
$non_deleted_tids = $this->bo->content_types;
unset($non_deleted_tids[addressbook_so::DELETED_TYPE]);
unset($non_deleted_tids[Api\Contacts::DELETED_TYPE]);
$non_deleted_tids = array_keys($non_deleted_tids);
}
$contact = $this->bo->read(array(self::$path_attr => $id, 'tid' => $non_deleted_tids));
@ -1000,7 +1002,7 @@ class addressbook_groupdav extends groupdav_handler
$contact = null;
}
if ($contact && $contact['tid'] == addressbook_so::DELETED_TYPE)
if ($contact && $contact['tid'] == Api\Contacts::DELETED_TYPE)
{
$contact = null; // handle deleted events, as not existing (404 Not Found)
}

View File

@ -1,15 +1,16 @@
<?php
/**
* eGroupWare
* EGroupware Addressbook
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package importexport
* @link http://www.egroupware.org
* @author Cornelius Weiss <nelius@cwtech.de>
* @copyright Cornelius Weiss <nelius@cwtech.de>
* @version $Id: $
* @version $Id$
*/
use EGroupware\Api;
/**
* class import_csv for addressbook
@ -30,6 +31,8 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
/**
* For figuring out if a contact has changed
*
* @var Api\Contacts\Tracking
*/
protected $tracking;
@ -48,10 +51,10 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
public function init(importexport_definition &$_definition ) {
// fetch the addressbook bo
$this->bocontacts = new addressbook_bo();
$this->bocontacts = new Api\Contacts();
// Get the tracker for changes
$this->tracking = new addressbook_tracking($this->bocontacts);
$this->tracking = new Api\Contacts\Tracking($this->bocontacts);
$this->lookups = array(
'tid' => array('n'=>'contact')
@ -138,10 +141,10 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
// Also handle categories in their own field
$record_array = $record->get_record_array();
$more_categories = array();
foreach($this->definition->plugin_options['field_mapping'] as $number => $field_name) {
foreach($this->definition->plugin_options['field_mapping'] as $field_name) {
if(!array_key_exists($field_name, $record_array) ||
substr($field_name,0,3) != 'cat' || !$record->$field_name || $field_name == 'cat_id') continue;
list($cat, $cat_id) = explode('-', $field_name);
list(, $cat_id) = explode('-', $field_name);
if(is_numeric($record->$field_name) && $record->$field_name != 1) {
// Column has a single category ID
$more_categories[] = $record->$field_name;
@ -175,7 +178,7 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
if($record_array[$condition['string']]) {
$searchcondition = array( $condition['string'] => $record_array[$condition['string']]);
// if we use account_id for the condition, we need to set the owner for filtering, as this
// enables addressbook_so to decide what backend is to be used
// enables Api\Contacts\Storage to decide what backend is to be used
if ($condition['string']=='account_id') $searchcondition['owner']=0;
$contacts = $this->bocontacts->search(
//array( $condition['string'] => $record[$condition['string']],),
@ -204,7 +207,7 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
break;
case 'equal':
// Match on field
$result = $this->equal($record, $condition, $matches);
$result = $this->equal($record, $condition);
if($result)
{
// Apply true action to any matching records found
@ -270,7 +273,8 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
$old = $this->bocontacts->read($_data['id']);
// if we get countrycodes as countryname, try to translate them -> the rest should be handled by bo classes.
foreach(array('adr_one_', 'adr_two_') as $c_prefix) {
if (strlen(trim($_data[$c_prefix.'countryname']))==2) $_data[$c_prefix.'countryname'] = $GLOBALS['egw']->country->get_full_name(trim($_data[$c_prefix.'countryname']),$translated=true);
if (strlen(trim($_data[$c_prefix.'countryname']))==2)
$_data[$c_prefix.'countryname'] = $GLOBALS['egw']->country->get_full_name(trim($_data[$c_prefix.'countryname']), true);
}
// Don't change a user account into a contact
if($old['owner'] == 0) {
@ -412,4 +416,3 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
return $this->results;
}
} // end of iface_export_plugin
?>

File diff suppressed because it is too large Load Diff

View File

@ -1138,9 +1138,9 @@ window.egw_LAB.wait(function() {
if ($contact['owner'] || // regular contact or
empty($contact['account_id']) || // accounts without account_id
// already deleted account (should no longer happen, but needed to allow for cleanup)
$contact['tid'] == addressbook_so::DELETED_TYPE)
$contact['tid'] == self::DELETED_TYPE)
{
$Ok = $this->delete($id, $contact['tid'] != addressbook_so::DELETED_TYPE && $contact['account_id']);
$Ok = $this->delete($id, $contact['tid'] != self::DELETED_TYPE && $contact['account_id']);
}
// delete single account --> redirect to admin
elseif (count($checked) == 1 && $contact['account_id'])
@ -1684,7 +1684,7 @@ window.egw_LAB.wait(function() {
{
$row['class'] .= 'rowAccount rowNoDelete ';
}
elseif (!$this->check_perms(EGW_ACL_DELETE,$row) || (!$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge' && $query['col_filter']['tid'] == addressbook_so::DELETED_TYPE))
elseif (!$this->check_perms(EGW_ACL_DELETE,$row) || (!$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge' && $query['col_filter']['tid'] == self::DELETED_TYPE))
{
$row['class'] .= 'rowNoDelete ';
}
@ -2274,7 +2274,7 @@ window.egw_LAB.wait(function() {
);
// Links for deleted entries
if($content['tid'] == addressbook_so::DELETED_TYPE)
if($content['tid'] == self::DELETED_TYPE)
{
$content['link_to']['show_deleted'] = true;
if(!$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge')
@ -2604,7 +2604,7 @@ window.egw_LAB.wait(function() {
'to_id' => $content['id'],
);
// Links for deleted entries
if($content['tid'] == addressbook_so::DELETED_TYPE)
if($content['tid'] == self::DELETED_TYPE)
{
$content['link_to']['show_deleted'] = true;
}

View File

@ -1,6 +1,6 @@
<?php
/**
* eGgroupWare admin - UI for adding custom fields
* EGgroupware admin - UI for adding custom fields
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
@ -10,6 +10,8 @@
* @version $Id$
*/
use EGroupware\Api;
/**
* Customfields class - manages customfield definitions in egw_config table
*
@ -103,10 +105,10 @@ class customfields
{
if (($this->appname = $appname))
{
$this->fields = egw_customfields::get($this->appname,true);
$this->content_types = config::get_content_types($this->appname);
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->content_types = Api\Config::get_content_types($this->appname);
}
$this->so = new so_sql('phpgwapi','egw_customfields',null,'',true);
$this->so = new Api\Storage\Base('phpgwapi','egw_customfields',null,'',true);
}
/**
@ -121,7 +123,7 @@ class customfields
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = egw_customfields::get($this->appname,true);
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->tmpl = new etemplate_new();
$this->tmpl->read('admin.customfields');
@ -135,7 +137,7 @@ class customfields
{
if(count($this->content_types) == 0)
{
$this->content_types = config::get_content_types($this->appname);
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
@ -152,7 +154,7 @@ class customfields
}
elseif($content['content_types']['create'])
{
if($new_type = $this->create_content_type($content))
if(($new_type = $this->create_content_type($content)))
{
$content['content_types']['types'] = $this->content_type = $new_type;
}
@ -214,7 +216,6 @@ class customfields
$content['type_template'] = $this->appname . '.admin.types';
$content['content_types']['appname'] = $this->appname;
$content_types = array_keys($this->content_types);
$content['content_type_options'] = $this->content_types[$this->content_type]['options'];
$content['content_type_options']['type'] = $this->types2[$this->content_type];
@ -251,6 +252,7 @@ class customfields
);
// Allow extending app a change to change content before display
$readonlys = null;
static::app_index($content, $sel_options, $readonlys, $preserve);
// Make sure app css gets loaded, extending app might cause et2 to miss it
@ -292,7 +294,7 @@ class customfields
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = egw_customfields::get($this->appname,true);
$this->fields = Api\Storage\Customfields::get($this->appname,true);
// Update based on info returned from template
if (is_array($content))
@ -328,8 +330,8 @@ class customfields
{
foreach(explode("\n",trim($content['cf_values'])) as $line)
{
list($var,$value) = explode('=',trim($line),2);
$var = trim($var);
list($var_raw,$value) = explode('=',trim($line),2);
$var = trim($var_raw);
$values[$var] = trim($value)==='' ? $var : $value;
}
}
@ -343,10 +345,10 @@ class customfields
$update_content[substr($key,3)] = $value;
}
}
egw_customfields::update($update_content);
Api\Storage\Customfields::update($update_content);
if(!$cf_id)
{
$this->fields = egw_customfields::get($this->appname,true);
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$cf_id = (int)$this->fields[$content['cf_name']]['id'];
}
egw_framework::refresh_opener('Saved', 'admin', $cf_id, 'edit');
@ -415,7 +417,7 @@ class customfields
{
if(count($this->content_types) == 0)
{
$this->content_types = config::get_content_types($this->appname);
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
@ -455,6 +457,7 @@ class customfields
*/
protected function app_index(&$content, &$sel_options, &$readonlys)
{
unset($content, $sel_options, $readonlys); // not used, as this is a stub
// This is just a stub.
}
@ -494,78 +497,6 @@ class customfields
return $actions;
}
function update_fields(&$content)
{
foreach($content['fields'] as $field)
{
$name = trim($field['name']);
$old_name = $field['old_name'];
if (!empty($delete) && $delete == $old_name)
{
unset($this->fields[$old_name]);
continue;
}
if (isset($field['old_name']))
{
if (empty($name)) // empty name not allowed
{
$content['error_msg'] = lang('Name must not be empty !!!');
$name = $old_name;
}
if (!empty($name) && $old_name != $name) // renamed
{
unset($this->fields[$old_name]);
}
}
elseif (empty($name)) // new item and empty ==> ignore it
{
continue;
}
$values = array();
if (!empty($field['values']))
{
foreach(explode("\n",$field['values']) as $line)
{
list($var,$value) = explode('=',trim($line),2);
$var = trim($var);
$values[$var] = empty($value) ? $var : $value;
}
}
$this->fields[$name] = array(
'type' => $field['type'],
'type2' => $field['type2'],
'label' => empty($field['label']) ? $name : $field['label'],
'help' => $field['help'],
'values'=> $values,
'len' => $field['len'],
'rows' => (int)$field['rows'],
'order' => (int)$field['order'],
'private' => $field['private'],
'needed' => $field['needed'],
);
if(!$this->fields[$name]['type2'] && $this->manage_content_types)
{
$this->fields[$name]['type2'] = (string)0;
}
}
if (!function_exists('sort_by_order'))
{
function sort_by_order($arr1,$arr2)
{
return $arr1['order'] - $arr2['order'];
}
}
uasort($this->fields,sort_by_order);
$n = 0;
foreach($this->fields as $name => $data)
{
$this->fields[$name]['order'] = ($n += 10);
}
}
function update(&$content)
{
$this->content_types[$this->content_type]['options'] = $content['content_type_options'];
@ -627,7 +558,7 @@ class customfields
}
else
{
foreach($this->content_types as $letter => $type)
foreach($this->content_types as $type)
{
if($type['name'] == $new_name)
{
@ -640,8 +571,8 @@ class customfields
{
if (!$this->content_types[chr($i)] &&
// skip letter of deleted type for addressbook content-types, as it gives SQL error
// content-type are lowercase, addressbook_so::DELETED_TYPE === 'D', but DB is case-insensitive
($this->appname !== 'addressbook' || chr($i) !== strtolower(addressbook_so::DELETED_TYPE)))
// content-type are lowercase, Api\Contacts::DELETED_TYPE === 'D', but DB is case-insensitive
($this->appname !== 'addressbook' || chr($i) !== strtolower(Api\Contacts::DELETED_TYPE)))
{
$new_type = chr($i);
break;
@ -664,32 +595,32 @@ class customfields
$config->value('types',$this->content_types);
$config->save_repository();
egw_customfields::save($this->appname, $this->fields);
Api\Storage\Customfields::save($this->appname, $this->fields);
}
/**
* get customfields of using application
*
* @deprecated use egw_customfields::get() direct, no need to instanciate this UI class
* @deprecated use Api\Storage\Customfields::get() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @param boolean $all_private_too=false should all the private fields be returned too
* @param boolean $all_private_too =false should all the private fields be returned too
* @return array with customfields
*/
function get_customfields($all_private_too=false)
{
return egw_customfields::get($this->appname,$all_private_too);
return Api\Storage\Customfields::get($this->appname,$all_private_too);
}
/**
* get_content_types of using application
*
* @deprecated use config::get_content_types() direct, no need to instanciate this UI class
* @deprecated use Api\Config::get_content_types() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @return array with content-types
*/
function get_content_types()
{
return config::get_content_types($this->appname);
return Api\Config::get_content_types($this->appname);
}
/**

View File

@ -228,13 +228,13 @@ class Config
* @param string $app
* @param boolean $all_private_too =false should all the private fields be returned too, default no
* @param string $only_type2 =null if given only return fields of type2 == $only_type2
* @deprecated use Api\Customfields::get()
* @deprecated use Api\Storage\Customfields::get()
* @return array with customfields
*/
static function get_customfields($app, $all_private_too=false, $only_type2=null)
{
//error_log(__METHOD__."('$app', $all_private_too, $only_type2) deprecated, use Customfields::get() in ". function_backtrace());
return Customfields::get($app, $all_private_too, $only_type2);
//error_log(__METHOD__."('$app', $all_private_too, $only_type2) deprecated, use Storage\Customfields::get() in ". function_backtrace());
return Storage\Customfields::get($app, $all_private_too, $only_type2);
}
/**

2402
api/src/Contacts.php Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,22 @@
<?php
/**
* Addressbook - ADS Backend
* EGroupware API: Contacts ADS Backend
*
* @link http://www.egroupware.org
* @author Ralf Becker <rb@stylite.de>
* @package addressbook
* @package api
* @subpackage contacts
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Contacts;
use EGroupware\Api;
// explicitly reference classes still in phpgwapi
use accounts_ads;
/**
* Active directory backend for accounts (not yet AD contacts)
*
@ -22,7 +30,7 @@
* All values used to construct filters need to run through ldap::quote(),
* to be save against LDAP query injection!!!
*/
class addressbook_ads extends addressbook_ldap
class Ads extends Ldap
{
/**
* LDAP searches only a limited set of attributes for performance reasons,
@ -74,11 +82,13 @@ class addressbook_ads extends addressbook_ldap
/**
* constructor of the class
*
* @param array $ldap_config=null default use from $GLOBALS['egw_info']['server']
* @param resource $ds=null ldap connection to use
* @param array $ldap_config =null default use from $GLOBALS['egw_info']['server']
* @param resource $ds =null ldap connection to use
*/
function __construct(array $ldap_config=null, $ds=null)
{
if (false) parent::__construct (); // quiten IDE warning, we are explicitly NOT calling parrent constructor!
$this->accountName = $GLOBALS['egw_info']['user']['account_lid'];
if ($ldap_config)
@ -103,8 +113,8 @@ class addressbook_ads extends addressbook_ldap
{
$this->connect();
}
$this->ldapServerInfo = ldapserverinfo::get($this->ds, $this->ldap_config['ads_host']);
$this->is_samba4 = $this->ldapServerInfo->serverType == SAMBA4_LDAPSERVER;
$this->ldapServerInfo = Api\Ldap\ServerInfo::get($this->ds, $this->ldap_config['ads_host']);
$this->is_samba4 = $this->ldapServerInfo->serverType == Api\Ldap\ServerInfo::SAMBA4;
// AD seems to use user, instead of inetOrgPerson
unset($this->schema2egw['posixaccount']);
@ -118,22 +128,24 @@ class addressbook_ads extends addressbook_ldap
unset($this->schema2egw['user']['n_fileas']);
unset($this->schema2egw['inetorgperson']);
foreach($this->schema2egw as $schema => $attributes)
foreach($this->schema2egw as $attributes)
{
$this->all_attributes = array_merge($this->all_attributes,array_values($attributes));
}
$this->all_attributes = array_values(array_unique($this->all_attributes));
$this->charset = translation::charset();
$this->charset = Api\Translation::charset();
}
/**
* connect to LDAP server
*
* @param boolean $admin=false true (re-)connect with admin not user credentials, eg. to modify accounts
* @param boolean $admin =false true (re-)connect with admin not user credentials, eg. to modify accounts
*/
function connect($admin=false)
{
unset($admin); // not used, but required by function signature
$this->ds = $this->accounts_ads->ldap_connection();
}
@ -159,21 +171,21 @@ class addressbook_ads extends addressbook_ldap
/**
* reads contact data
*
* @param string/array $contact_id contact_id or array with values for id or account_id
* @param string/array $_contact_id contact_id or array with values for id or account_id
* @return array/boolean data if row could be retrived else False
*/
function read($contact_id)
function read($_contact_id)
{
if (is_array($contact_id) && isset($contact_id['account_id']) ||
!is_array($contact_id) && substr($contact_id,0,8) == 'account:')
if (is_array($_contact_id) && isset($_contact_id['account_id']) ||
!is_array($_contact_id) && substr($_contact_id,0,8) == 'account:')
{
$account_id = (int)(is_array($contact_id) ? $contact_id['account_id'] : substr($contact_id,8));
$contact_id = $GLOBALS['egw']->accounts->id2name($account_id, 'person_id');
$account_id = (int)(is_array($_contact_id) ? $_contact_id['account_id'] : substr($_contact_id,8));
$_contact_id = $GLOBALS['egw']->accounts->id2name($account_id, 'person_id');
}
$contact_id = !is_array($contact_id) ? $contact_id :
(isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid']);
$contact_id = !is_array($_contact_id) ? $_contact_id :
(isset ($_contact_id['id']) ? $_contact_id['id'] : $_contact_id['uid']);
$rows = $this->_searchLDAP($this->allContactsDN, $filter=$this->id_filter($contact_id), $this->all_attributes, ADDRESSBOOK_ALL);
$rows = $this->_searchLDAP($this->allContactsDN, $filter=$this->id_filter($contact_id), $this->all_attributes, Ldap::ALL);
//error_log(__METHOD__."('$contact_id') _searchLDAP($this->allContactsDN, '$filter',...)=".array2string($rows));
return $rows ? $rows[0] : false;
}
@ -214,5 +226,4 @@ class addressbook_ads extends addressbook_ldap
parent::sanitize_update($ldapContact);
}
}

View File

@ -1,20 +1,23 @@
<?php
/**
* Addressbook - LDAP Backend
* EGroupware API: Contacts LDAP Backend
*
* @link http://www.egroupware.org
* @author Cornelius Weiss <egw-AT-von-und-zu-weiss.de>
* @author Lars Kneschke <l.kneschke-AT-metaways.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @package api
* @subpackage contacts
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
define('ADDRESSBOOK_ALL',0);
define('ADDRESSBOOK_ACCOUNTS',1);
define('ADDRESSBOOK_PERSONAL',2);
define('ADDRESSBOOK_GROUP',3);
namespace EGroupware\Api\Contacts;
use EGroupware\Api;
// explicitly reference classes still in phpgwapi
use common; // randomstring
/**
* LDAP Backend for contacts, compatible with vars and parameters of eTemplate's so_sql.
@ -23,8 +26,14 @@ define('ADDRESSBOOK_GROUP',3);
* All values used to construct filters need to run through ldap::quote(),
* to be save against LDAP query injection!!!
*/
class addressbook_ldap
class Ldap
{
const ALL = 0;
const ACCOUNTS = 1;
const PERSONAL = 2;
const GROUP = 3;
var $data;
/**
@ -298,7 +307,7 @@ class addressbook_ldap
}
$this->all_attributes = array_values(array_unique($this->all_attributes));
$this->charset = translation::charset();
$this->charset = Api\Translation::charset();
}
/**
@ -414,7 +423,7 @@ class addressbook_ldap
$filter = $this->id_filter($contact_id);
}
$rows = $this->_searchLDAP($this->allContactsDN,
$filter, $this->all_attributes, ADDRESSBOOK_ALL, array('_posixaccount2egw'));
$filter, $this->all_attributes, self::ALL, array('_posixaccount2egw'));
return $rows ? $rows[0] : false;
}
@ -508,7 +517,7 @@ class addressbook_ldap
if(empty($contactUID))
{
$ldapContact[$this->dn_attribute] = $this->data[$this->contacts_id] = $contactUID = md5($GLOBALS['egw']->common->randomstring(15));
$ldapContact[$this->dn_attribute] = $this->data[$this->contacts_id] = $contactUID = md5(common::randomstring(15));
}
//error_log(__METHOD__."() contactUID='$contactUID', isUpdate=".array2string($isUpdate).", oldContactInfo=".array2string($oldContactInfo));
// add for all supported objectclasses the objectclass and it's attributes
@ -537,7 +546,7 @@ class addressbook_ldap
{
// dont convert the (binary) jpegPhoto!
$ldapContact[$ldapFieldName] = $ldapFieldName == 'jpegphoto' ? $data[$egwFieldName] :
translation::convert(trim($data[$egwFieldName]),$this->charset,'utf-8');
Api\Translation::convert(trim($data[$egwFieldName]),$this->charset,'utf-8');
}
elseif($isUpdate && isset($data[$egwFieldName]))
{
@ -593,7 +602,7 @@ class addressbook_ldap
{
$result = ldap_read($this->ds, $dn, 'objectclass=*');
$entries = ldap_get_entries($this->ds, $result);
$oldContact = ldap::result2array($entries[0]);
$oldContact = Api\Ldap::result2array($entries[0]);
unset($oldContact['dn']);
$newContact = $oldContact;
@ -681,7 +690,7 @@ class addressbook_ldap
foreach($keys as $entry)
{
$entry = ldap::quote(is_array($entry) ? $entry['id'] : $entry);
$entry = Api\Ldap::quote(is_array($entry) ? $entry['id'] : $entry);
if(($result = ldap_search($this->ds, $this->allContactsDN,
"(|(entryUUID=$entry)(uid=$entry))", $attributes)))
{
@ -759,33 +768,33 @@ class addressbook_ldap
{
if (!($accountName = $GLOBALS['egw']->accounts->id2name($filter['owner']))) return false;
$searchDN = 'cn='. ldap::quote(strtolower($accountName)) .',';
$searchDN = 'cn='. Api\Ldap::quote(strtolower($accountName)) .',';
if ($filter['owner'] < 0)
{
$searchDN .= $this->sharedContactsDN;
$addressbookType = ADDRESSBOOK_GROUP;
$addressbookType = self::GROUP;
}
else
{
$searchDN .= $this->personalContactsDN;
$addressbookType = ADDRESSBOOK_PERSONAL;
$addressbookType = self::PERSONAL;
}
}
elseif (!isset($filter['owner']))
{
$searchDN = $this->allContactsDN;
$addressbookType = ADDRESSBOOK_ALL;
$addressbookType = self::ALL;
}
else
{
$searchDN = $this->accountContactsDN;
$addressbookType = ADDRESSBOOK_ACCOUNTS;
$addressbookType = self::ACCOUNTS;
}
// create the search filter
switch($addressbookType)
{
case ADDRESSBOOK_ACCOUNTS:
case self::ACCOUNTS:
$objectFilter = $this->accountsFilter;
break;
default:
@ -813,8 +822,8 @@ class addressbook_ldap
{
if(($ldapSearchKey = $mapping[$egwSearchKey]))
{
$searchString = translation::convert($searchValue,$this->charset,'utf-8');
$searchFilter .= '('.$ldapSearchKey.'='.$wildcard.ldap::quote($searchString).$wildcard.')';
$searchString = Api\Translation::convert($searchValue,$this->charset,'utf-8');
$searchFilter .= '('.$ldapSearchKey.'='.$wildcard.Api\Ldap::quote($searchString).$wildcard.')';
break;
}
}
@ -915,7 +924,7 @@ class addressbook_ldap
}
elseif ($value)
{
$filters .= '(uidNumber='.ldap::quote($value).')';
$filters .= '(uidNumber='.Api\Ldap::quote($value).')';
}
break;
@ -935,9 +944,9 @@ class addressbook_ldap
if (count($cats) > 1) $filters .= '(|';
foreach($cats as $cat)
{
$catName = translation::convert(
$catName = Api\Translation::convert(
$GLOBALS['egw']->categories->id2name($cat),$this->charset,'utf-8');
$filters .= '(category='.ldap::quote($catName).')';
$filters .= '(category='.Api\Ldap::quote($catName).')';
}
if (count($cats) > 1) $filters .= ')';
}
@ -965,13 +974,13 @@ class addressbook_ldap
{
// todo: value = "!''"
$filters .= '('.$mapping[$key].'='.($value === "!''" ? '*' :
ldap::quote(translation::convert($value,$this->charset,'utf-8'))).')';
Api\Ldap::quote(Api\Translation::convert($value,$this->charset,'utf-8'))).')';
break;
}
}
}
// filter for letter-search
elseif (preg_match("/^([^ ]+) ".preg_quote($GLOBALS['egw']->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE])." '(.*)%'$/",$value,$matches))
elseif (preg_match("/^([^ ]+) ".preg_quote($GLOBALS['egw']->db->capabilities[Api\Db::CAPABILITY_CASE_INSENSITIV_LIKE])." '(.*)%'$/",$value,$matches))
{
list(,$name,$value) = $matches;
if (strpos($name,'.') !== false) list(,$name) = explode('.',$name);
@ -979,8 +988,8 @@ class addressbook_ldap
{
if (isset($mapping[$name]))
{
$filters .= '('.$mapping[$name].'='.ldap::quote(
translation::convert($value,$this->charset,'utf-8')).'*)';
$filters .= '('.$mapping[$name].'='.Api\Ldap::quote(
Api\Translation::convert($value,$this->charset,'utf-8')).'*)';
break;
}
}
@ -1017,7 +1026,7 @@ class addressbook_ldap
//error_log(__METHOD__."('$_ldapContext', '$_filter', ".array2string($_attributes).", $_addressbooktype)");
if($_addressbooktype == ADDRESSBOOK_ALL || $_ldapContext == $this->allContactsDN)
if($_addressbooktype == self::ALL || $_ldapContext == $this->allContactsDN)
{
$result = ldap_search($this->ds, $_ldapContext, $_filter, $_attributes, 0, $this->ldapLimit);
}
@ -1048,7 +1057,7 @@ class addressbook_ldap
{
if(!empty($entry[$ldapFieldName][0]) && !is_int($egwFieldName) && !isset($contact[$egwFieldName]))
{
$contact[$egwFieldName] = translation::convert($entry[$ldapFieldName][0],'utf-8');
$contact[$egwFieldName] = Api\Translation::convert($entry[$ldapFieldName][0],'utf-8');
}
}
$objectclass2egw = '_'.$objectclass.'2egw';
@ -1206,7 +1215,7 @@ class addressbook_ldap
$ldapContact['category'] = array();
foreach(is_array($data['cat_id']) ? $data['cat_id'] : explode(',',$data['cat_id']) as $cat)
{
$ldapContact['category'][] = translation::convert(
$ldapContact['category'][] = Api\Translation::convert(
ExecMethod('phpgwapi.categories.id2name',$cat),$this->charset,'utf-8');
}
}
@ -1217,7 +1226,7 @@ class addressbook_ldap
{
if($value != '$, $$$')
{
$ldapContact[$attr] = translation::convert($value,$this->charset,'utf-8');
$ldapContact[$attr] = Api\Translation::convert($value,$this->charset,'utf-8');
}
elseif($isUpdate)
{
@ -1421,6 +1430,6 @@ class addressbook_ldap
*/
function change_owner($account_id,$new_owner)
{
error_log("so_ldap::change_owner($account_id,$new_owner) not yet implemented");
error_log(__METHOD__."($account_id,$new_owner) not yet implemented");
}
}

View File

@ -1,19 +1,27 @@
<?php
/**
* EGroupware : Addressbook - SQL backend
* EGroupware API: Contacts - SQL storage
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @copyright (c) 2006-13 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package api
* @subpackage contacts
* @copyright (c) 2006-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Contacts;
use EGroupware\Api;
// explicitly reference classes still in phpgwapi
use common; // common::generate_uid
/**
* SQL storage object of the adressbook
* Contacts - SQL storage
*/
class addressbook_sql extends so_sql_cf
class Sql extends Api\Storage
{
/**
* name of custom fields table
@ -60,15 +68,15 @@ class addressbook_sql extends so_sql_cf
/**
* Constructor
*
* @param egw_db $db=null
* @param Api\Db $db =null
*/
function __construct(egw_db $db=null)
function __construct(Api\Db $db=null)
{
parent::__construct('phpgwapi', 'egw_addressbook', self::EXTRA_TABLE, 'contact_',
$extra_key='_name',$extra_value='_value',$extra_id='_id',$db);
parent::__construct('phpgwapi', 'egw_addressbook', self::EXTRA_TABLE,
'contact_', '_name', '_value', '_id', $db);
// Get custom fields from addressbook instead of phpgwapi
$this->customfields = config::get_customfields('addressbook');
$this->customfields = Api\Storage\Customfields::get('addressbook');
if ($GLOBALS['egw_info']['server']['account_repository'])
{
@ -146,7 +154,7 @@ class addressbook_sql extends so_sql_cf
}
if ($param['searchletter'])
{
$filter[] = 'org_name '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote($param['searchletter'].'%');
$filter[] = 'org_name '.$this->db->capabilities[Api\Db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote($param['searchletter'].'%');
}
else
{
@ -169,8 +177,8 @@ class addressbook_sql extends so_sql_cf
// org total for more then one $by
$by_expr = $by == 'org_unit_count' ? "COUNT(DISTINCT CASE WHEN org_unit IS NULL THEN '' ELSE org_unit END)" :
"COUNT(DISTINCT CASE WHEN adr_one_locality IS NULL THEN '' ELSE adr_one_locality END)";
$append = "GROUP BY org_name HAVING $by_expr > 1 ORDER BY org_name $sort";
parent::search($param['search'],array('org_name'),$append,array(
parent::search($param['search'],array('org_name'),
"GROUP BY org_name HAVING $by_expr > 1 ORDER BY org_name $sort", array(
"NULL AS $by",
'1 AS is_main',
'COUNT(DISTINCT egw_addressbook.contact_id) AS org_count',
@ -178,8 +186,8 @@ class addressbook_sql extends so_sql_cf
"COUNT(DISTINCT CASE WHEN adr_one_locality IS NULL THEN '' ELSE adr_one_locality END) AS adr_one_locality_count",
),$wildcard,false,$op/*'OR'*/,'UNION',$filter);
// org by location
$append = "GROUP BY org_name,$by ORDER BY org_name $sort,$by $sort";
parent::search($param['search'],array('org_name'),$append,array(
parent::search($param['search'],array('org_name'),
"GROUP BY org_name,$by ORDER BY org_name $sort,$by $sort", array(
"CASE WHEN $by IS NULL THEN '' ELSE $by END AS $by",
'0 AS is_main',
'COUNT(DISTINCT egw_addressbook.contact_id) AS org_count',
@ -195,7 +203,7 @@ class addressbook_sql extends so_sql_cf
// query the values for *_count == 1, to display them instead
$filter['org_name'] = $orgs = array();
foreach($rows as $n => $row)
foreach($rows as $row)
{
if ($row['org_unit_count'] == 1 || $row['adr_one_locality_count'] == 1)
{
@ -208,7 +216,8 @@ class addressbook_sql extends so_sql_cf
if (count($filter['org_name']))
{
foreach((array) parent::search($criteria,array('org_name','org_unit','adr_one_locality'),'GROUP BY org_name,org_unit,adr_one_locality',
foreach((array) parent::search(null, array('org_name','org_unit','adr_one_locality'),
'GROUP BY org_name,org_unit,adr_one_locality',
'',$wildcard,false,$op/*'AND'*/,false,$filter) as $row)
{
$org_key = $row['org_name'].($by ? '|||'.$row[$by] : '');
@ -243,19 +252,19 @@ class addressbook_sql extends so_sql_cf
*
* For a union-query you call search for each query with $start=='UNION' and one more with only $order_by and $start set to run the union-query.
*
* @param array/string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!)
* @param boolean/string/array $only_keys=true True returns only keys, False returns all cols. or
* @param array|string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!)
* @param boolean|string|array $only_keys =true True returns only keys, False returns all cols. or
* comma seperated list or array of columns 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), or 'UNION' for a part of a union query
* @param array $filter=null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards
* @param string $join='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or
* @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), or 'UNION' for a part of a union query
* @param array $filter =null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards
* @param string $join ='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or
* "LEFT JOIN table2 ON (x=y)", Note: there's no quoting done on $join!
* @param boolean $need_full_no_count=false If true an unlimited query is run to determine the total number of rows, default false
* @param boolean $need_full_no_count =false If true an unlimited query is run to determine the total number of rows, default false
* @return boolean/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='',$need_full_no_count=false)
@ -360,7 +369,9 @@ class addressbook_sql extends so_sql_cf
// fall through
}
// postgres requires that expressions in order by appear in the columns of a distinct select
if ($this->db->Type != 'mysql' && preg_match_all("/(#?[a-zA-Z_.]+) *(<> *''|IS NULL|IS NOT NULL)? *(ASC|DESC)?(,|$)/ui",$order_by,$all_matches,PREG_SET_ORDER))
$all_matches = null;
if ($this->db->Type != 'mysql' && preg_match_all("/(#?[a-zA-Z_.]+) *(<> *''|IS NULL|IS NOT NULL)? *(ASC|DESC)?(,|$)/ui",
$order_by, $all_matches, PREG_SET_ORDER))
{
if (!is_array($extra_cols)) $extra_cols = $extra_cols ? explode(',',$extra_cols) : array();
foreach($all_matches as $matches)
@ -394,10 +405,12 @@ class addressbook_sql extends so_sql_cf
// Understand search by date with wildcard (????.10.??) according to user date preference
if(is_string($criteria) && strpos($criteria, '?') !== false)
{
$date_format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'];
// First, check for a 'date', with wildcards, in the user's format
$date_regex = str_replace(array('Y','m','d','.','-'), array('(?P<Y>(?:\?|\Q){4})','(?P<m>(?:\?|\Q){2})','(?P<d>(?:\?|\Q){2})','\.','\-'),$date_format);
$date_regex = str_replace('Q','d',$date_regex);
$date_regex = str_replace('Q','d',
str_replace(array('Y','m','d','.','-'),
array('(?P<Y>(?:\?|\Q){4})','(?P<m>(?:\?|\Q){2})','(?P<d>(?:\?|\Q){2})','\.','\-'),
$GLOBALS['egw_info']['user']['preferences']['common']['dateformat']));
if(preg_match_all('$'.$date_regex.'$', $criteria, $matches))
{
foreach($matches[0] as $m_id => $match)
@ -467,7 +480,7 @@ class addressbook_sql extends so_sql_cf
{
if (!$new_owner) // otherwise we would create an account (contact_owner==0)
{
throw egw_exception_wrong_parameter(__METHOD__."($account_id, $new_owner) new owner must not be 0!");
throw Api\Exception\WrongParameter(__METHOD__."($account_id, $new_owner) new owner must not be 0!");
}
// contacts
$this->db->update($this->table_name,array(
@ -496,9 +509,9 @@ class addressbook_sql extends so_sql_cf
*
* @param array $uids array of user or group id's for $uid_column='list_owners', or values for $uid_column,
* or whole where array: column-name => value(s) pairs
* @param string $uid_column='list_owner' column-name or null to use $uids as where array
* @param string $member_attr=null null: no members, 'contact_uid', 'contact_id', 'caldav_name' return members as that attribute
* @param boolean|int|array $limit_in_ab=false if true only return members from the same owners addressbook,
* @param string $uid_column ='list_owner' column-name or null to use $uids as where array
* @param string $member_attr =null null: no members, 'contact_uid', 'contact_id', 'caldav_name' return members as that attribute
* @param boolean|int|array $limit_in_ab =false if true only return members from the same owners addressbook,
* if int|array only return members from the given owners addressbook(s)
* @return array with list_id => array(list_id,list_name,list_owner,...) pairs
*/
@ -547,7 +560,7 @@ class addressbook_sql extends so_sql_cf
*
* @param string|array $keys list-name or array with column-name => value pairs to specify the list
* @param int $owner user- or group-id
* @param array $contacts=array() contacts to add (only for not yet existing lists!)
* @param array $contacts =array() contacts to add (only for not yet existing lists!)
* @param array &$data=array() values for keys 'list_uid', 'list_carddav_name', 'list_name'
* @return int|boolean integer list_id or false on error
*/
@ -606,7 +619,7 @@ class addressbook_sql extends so_sql_cf
*
* @param int|array $contact contact_id(s)
* @param int $list list-id
* @param array $existing=null array of existing contact-id(s) of list, to not reread it, eg. array()
* @param array $existing =null array of existing contact-id(s) of list, to not reread it, eg. array()
* @return false on error
*/
function add2list($contact,$list,array $existing=null)
@ -648,7 +661,7 @@ class addressbook_sql extends so_sql_cf
* Removes one contact from distribution list(s)
*
* @param int|array $contact contact_id(s)
* @param int $list=null list-id or null to remove from all lists
* @param int $list =null list-id or null to remove from all lists
* @return false on error
*/
function remove_from_list($contact,$list=null)
@ -690,7 +703,7 @@ class addressbook_sql extends so_sql_cf
/**
* Deletes a distribution list (incl. it's members)
*
* @param int/array $list list_id(s)
* @param int|array $list list_id(s)
* @return number of members deleted or false if list does not exist
*/
function delete_list($list)
@ -705,7 +718,7 @@ class addressbook_sql extends so_sql_cf
/**
* Get ctag (max list_modified as timestamp) for lists
*
* @param int|array $owner=null null for all lists user has access too
* @param int|array $owner =null null for all lists user has access too
* @return int
*/
function lists_ctag($owner=null)
@ -745,7 +758,7 @@ class addressbook_sql extends so_sql_cf
}
// 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
catch(egw_exception_db $e) {
catch(Api\Db\Exception $e) {
_egw_log_exception($e);
return false;
}
@ -762,11 +775,13 @@ class addressbook_sql extends so_sql_cf
* Saves a contact, reimplemented to check a given etag and set a uid
*
* @param array $keys if given $keys are copied to data before saveing => allows a save as
* @param string|array $extra_where=null extra where clause, eg. to check the etag, returns 'nothing_affected' if not affected rows
* @param string|array $extra_where =null extra where clause, eg. to check the etag, returns 'nothing_affected' if not affected rows
* @return int 0 on success and errno != 0 else
*/
function save($keys = NULL, $extra_where = NULL)
{
unset($extra_where); // not used, but required by function signature
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) {
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
} else {

1138
api/src/Contacts/Storage.php Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,27 @@
<?php
/**
* Addressbook - history and notifications
* EGroupware API - Contacts history and notifications
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @copyright (c) 2007 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package api
* @subpackage contacts
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Contacts;
use EGroupware\Api;
// explicitly reference classes still in phpgwapi
use common;
/**
* Addressbook - tracking object
* Contacts history and notifications
*/
class addressbook_tracking extends bo_tracking
class Tracking extends Api\Storage\Tracking
{
/**
* Application we are tracking (required!)
@ -64,10 +72,10 @@ class addressbook_tracking extends bo_tracking
/**
* Constructor
*
* @param addressbook_bo $bocontacts
* @param Api\Contacts $bocontacts
* @return tracker_tracking
*/
function __construct(addressbook_bo $bocontacts)
function __construct(Api\Contacts $bocontacts)
{
$this->contacts = $bocontacts;
@ -93,17 +101,19 @@ class addressbook_tracking extends bo_tracking
/**
* Get a notification-config value
*
* @param string $what
* @param string $name
* - 'copy' array of email addresses notifications should be copied too, can depend on $data
* - 'lang' string lang code for copy mail
* - 'sender' string send email address
* @param array $data current entry
* @param array $old=null old/last state of the entry or null for a new entry
* @param array $old =null old/last state of the entry or null for a new entry
* @return mixed
*/
function get_config($name,$data,$old=null)
{
//echo "<p>addressbook_tracking::get_config($name,".print_r($data,true).",...)</p>\n";
unset($old); // not used, but required by function signature
//echo "<p>".__METHOD__."($name,".print_r($data,true).",...)</p>\n";
switch($name)
{
case 'copy':
@ -134,9 +144,9 @@ class addressbook_tracking extends bo_tracking
*
* @internal use only track($data,$old)
* @param array $data current entry
* @param array $old=null old/last state of the entry or null for a new entry
* @param boolean $deleted=null can be set to true to let the tracking know the item got deleted or undelted
* @param array $changed_fields=null changed fields from ealier call to $this->changed_fields($data,$old), to not compute it again
* @param array $old =null old/last state of the entry or null for a new entry
* @param boolean $deleted =null can be set to true to let the tracking know the item got deleted or undelted
* @param array $changed_fields =null changed fields from ealier call to $this->changed_fields($data,$old), to not compute it again
* @return int number of log-entries made
*/
protected function save_history(array $data,array $old=null,$deleted=null,array $changed_fields=null)
@ -180,6 +190,8 @@ class addressbook_tracking extends bo_tracking
*/
protected function get_message($data,$old,$receiver=null)
{
unset($receiver); // not used, but required by function signature
if (!$data['modified'] || !$old)
{
return lang('New contact submitted by %1 at %2',
@ -196,12 +208,14 @@ class addressbook_tracking extends bo_tracking
*
* @param array $data
* @param array $old
* @param boolean $deleted=null can be set to true to let the tracking know the item got deleted or undelted
* @param boolean $deleted =null can be set to true to let the tracking know the item got deleted or undelted
* @param int|string $receiver nummeric account_id or email address
* @return string
*/
protected function get_subject($data,$old,$deleted=null,$receiver=null)
{
unset($old, $deleted, $receiver); // not used, but required by function signature
if ($data['is_contactform'])
{
$prefix = ($data['subject_contactform'] ? $data['subject_contactform'] : lang('Contactform')).': ';
@ -218,6 +232,8 @@ class addressbook_tracking extends bo_tracking
*/
function get_details($data,$receiver=null)
{
unset($receiver); // not used, but required by function signature
foreach($this->contacts->contact_fields as $name => $label)
{
if (!$data[$name] && $name != 'owner') continue;

View File

@ -1,20 +1,23 @@
<?php
/**
* Addressbook - Univention Backend
* EGroupware API: Contacts Univention Backend
*
* @link http://www.egroupware.org
* @author Ralf Becker <rb@stylite.de>
* @package addressbook
* @package api
* @subpackage contacts
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Contacts;
/**
* Univention backend for addressbook
*
* Different mail attribute is only difference to LDAP backend
*/
class addressbook_univention extends addressbook_ldap
class Univention extends Ldap
{
function __construct($ldap_config = null, $ds = null)
{

263
api/src/Ldap.php Normal file
View File

@ -0,0 +1,263 @@
<?php
/**
* EGroupware API - LDAP connection handling
*
* @link http://www.egroupware.org
* @author Lars Kneschke <l.kneschke@metaways.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage ldap
* @version $Id$
*/
namespace EGroupware\Api;
/**
* LDAP connection handling
*
* Please note for SSL or TLS connections hostname has to be:
* - SSL: "ldaps://host[:port]/"
* - TLS: "tls://host[:port]/"
* Both require certificats installed on the webserver, otherwise the connection will fail!
*
* If multiple (space-separated) ldap hosts or urls are given, try them in order and
* move first successful one to first place in session, to try not working ones
* only once per session.
*/
class Ldap
{
/**
* Holds the LDAP link identifier
*
* @var resource $ds
*/
var $ds;
/**
* Holds the detected information about the connected ldap server
*
* @var Ldap\ServerInfo $ldapserverinfo
*/
var $ldapserverinfo;
/**
* Throw Exceptions in ldapConnect instead of echoing error and returning false
*
* @var boolean $exception_on_error
*/
var $exception_on_error=false;
/**
* Constructor
*
* @param boolean $exception_on_error =false true: throw Exceptions in ldapConnect instead of echoing error and returning false
*/
function __construct($exception_on_error=false)
{
$this->exception_on_error = $exception_on_error;
$this->restoreSessionData();
}
/**
* Returns information about connected ldap server
*
* @return Ldap\ServerInfo|null
*/
function getLDAPServerInfo()
{
return $this->ldapserverinfo;
}
/**
* escapes a string for use in searchfilters meant for ldap_search.
*
* Escaped Characters are: '*', '(', ')', ' ', '\', NUL
* It's actually a PHP-Bug, that we have to escape space.
* For all other Characters, refer to RFC2254.
*
* @param string|array $string either a string to be escaped, or an array of values to be escaped
* @return string
*/
static function quote($string)
{
return str_replace(array('\\','*','(',')','\0',' '),array('\\\\','\*','\(','\)','\\0','\20'),$string);
}
/**
* Convert a single ldap result into a associative array
*
* @param array $ldap array with numerical and associative indexes and count's
* @return boolean|array with only associative index and no count's or false on error (parm is no array)
*/
static function result2array($ldap)
{
if (!is_array($ldap)) return false;
$arr = array();
foreach($ldap as $var => $val)
{
if (is_int($var) || $var == 'count') continue;
if (is_array($val) && $val['count'] == 1)
{
$arr[$var] = $val[0];
}
else
{
if (is_array($val)) unset($val['count']);
$arr[$var] = $val;
}
}
return $arr;
}
/**
* Connect to ldap server and return a handle
*
* If multiple (space-separated) ldap hosts or urls are given, try them in order and
* move first successful one to first place in session, to try not working ones
* only once per session.
*
* @param $host ='' ldap host, default $GLOBALS['egw_info']['server']['ldap_host']
* @param $dn ='' ldap dn, default $GLOBALS['egw_info']['server']['ldap_root_dn']
* @param $passwd ='' ldap pw, default $GLOBALS['egw_info']['server']['ldap_root_pw']
* @return resource|boolean resource from ldap_connect() or false on error
* @throws Exception\AssertingFailed 'LDAP support unavailable!' (no ldap extension)
*/
function ldapConnect($host='', $dn='', $passwd='')
{
if(!function_exists('ldap_connect'))
{
if ($this->exception_on_error) throw new Exception\AssertionFailed('LDAP support unavailable!');
printf('<b>Error: LDAP support unavailable</b><br>',$host);
return False;
}
if (empty($host))
{
$host = $GLOBALS['egw_info']['server']['ldap_host'];
}
if (empty($dn))
{
$dn = $GLOBALS['egw_info']['server']['ldap_root_dn'];
$passwd = $GLOBALS['egw_info']['server']['ldap_root_pw'];
}
// if multiple hosts given, try them all, but only once per session!
if (isset($_SESSION) && isset($_SESSION['ldapConnect']) && isset($_SESSION['ldapConnect'][$host]))
{
$host = $_SESSION['ldapConnect'][$host];
}
foreach($hosts=preg_split('/[ ,;]+/', $host) as $h)
{
if ($this->_connect($h, $dn, $passwd))
{
if ($h !== $host)
{
if (isset($_SESSION)) // store working host as first choice in session
{
$_SESSION['ldapConnect'][$host] = implode(' ',array_unique(array_merge(array($h),$hosts)));
}
}
return $this->ds;
}
error_log(__METHOD__."('$h', '$dn', \$passwd) Can't connect/bind to ldap server!".
($this->ds ? ' '.ldap_error($this->ds).' ('.ldap_errno($this->ds).')' : '').
' '.function_backtrace());
}
// give visible error, only if we cant connect to any ldap server
if ($this->exception_on_error) throw new Exception\NoPermission("Can't connect/bind to LDAP server '$host' and dn='$dn'!");
return false;
}
/**
* connect to the ldap server and return a handle
*
* @param string $host ldap host
* @param string $dn ldap dn
* @param string $passwd ldap pw
* @return resource|boolean resource from ldap_connect() or false on error
*/
private function _connect($host, $dn, $passwd)
{
if (($use_tls = substr($host,0,6) == 'tls://'))
{
$port = parse_url($host,PHP_URL_PORT);
$host = parse_url($host,PHP_URL_HOST);
}
// connect to ldap server (never fails, as connection happens in bind!)
if(!($this->ds = !empty($port) ? ldap_connect($host, $port) : ldap_connect($host)))
{
return False;
}
// set network timeout to not block for minutes
ldap_set_option($this->ds, LDAP_OPT_NETWORK_TIMEOUT, 5);
if(ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3))
{
$supportedLDAPVersion = 3;
}
else
{
$supportedLDAPVersion = 2;
}
if ($use_tls) ldap_start_tls($this->ds);
if (!isset($this->ldapserverinfo) ||
!is_a($this->ldapserverinfo,'EGroupware\Ldap\ServerInfo') ||
$this->ldapserverinfo->host != $host)
{
//error_log("no ldap server info found");
@ldap_bind($this->ds, $GLOBALS['egw_info']['server']['ldap_root_dn'], $GLOBALS['egw_info']['server']['ldap_root_pw']);
$this->ldapserverinfo = Ldap\ServerInfo::get($this->ds, $host, $supportedLDAPVersion);
$this->saveSessionData();
}
if(!@ldap_bind($this->ds, $dn, $passwd))
{
return False;
}
return $this->ds;
}
/**
* disconnect from the ldap server
*/
function ldapDisconnect()
{
if(is_resource($this->ds))
{
ldap_unbind($this->ds);
unset($this->ds);
unset($this->ldapserverinfo);
}
}
/**
* restore the session data
*/
function restoreSessionData()
{
if (isset($GLOBALS['egw']->session)) // no availible in setup
{
$this->ldapserverinfo = Cache::getSession(__CLASS__, 'ldapServerInfo');
}
}
/**
* save the session data
*/
function saveSessionData()
{
if (isset($GLOBALS['egw']->session)) // no availible in setup
{
Cache::getSession(__CLASS__, 'ldapServerInfo', $this->ldapserverinfo);
}
}
}

243
api/src/Ldap/ServerInfo.php Normal file
View File

@ -0,0 +1,243 @@
<?php
/**
* EGroupware API - LDAP server information
*
* @link http://www.egroupware.org
* @author Lars Kneschke <l.kneschke@metaways.de>
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage ldap
* @version $Id$
*/
namespace EGroupware\Api\Ldap;
/**
* Class to store and retrieve information (eg. supported object classes) of a connected ldap server
*/
class ServerInfo
{
/**
* Unknown LDAP server
*/
const UNKNOWN = 0;
/**
* OpenLDAP server
*/
const OPENLDAP = 1;
/**
* Samba4 LDAP server
*/
const SAMBA4 = 2;
/**
* @var array $namingContext holds the supported namingcontexts
*/
var $namingContext = array();
/**
* @var string $version holds the LDAP server version
*/
var $version = 2;
/**
* @var integer $serverType holds the type of LDAP server(OpenLDAP, ADS, NDS, ...)
*/
var $serverType = 0;
/**
* @var string $_subSchemaEntry the subschema entry DN
*/
var $subSchemaEntry = '';
/**
* @var array $supportedObjectClasses the supported objectclasses
*/
var $supportedObjectClasses = array();
/**
* @var array $supportedOIDs the supported OIDs
*/
var $supportedOIDs = array();
/**
* Name of host
*
* @var string
*/
var $host;
/**
* Constructor
*
* @param string $host
*/
function __construct($host)
{
$this->host = $host;
}
/**
* gets the version
*
* @return integer the supported ldap version
*/
function getVersion()
{
return $this->version;
}
/**
* sets the namingcontexts
*
* @param array $_namingContext the supported namingcontexts
*/
function setNamingContexts($_namingContext)
{
$this->namingContext = $_namingContext;
}
/**
* sets the type of the ldap server(OpenLDAP, ADS, NDS, ...)
*
* @param integer $_serverType the type of ldap server
*/
function setServerType($_serverType)
{
$this->serverType = $_serverType;
}
/**
* sets the DN for the subschema entry
*
* @param string $_subSchemaEntry the subschema entry DN
*/
function setSubSchemaEntry($_subSchemaEntry)
{
$this->subSchemaEntry = $_subSchemaEntry;
}
/**
* sets the supported objectclasses
*
* @param array $_supportedObjectClasses the supported objectclasses
*/
function setSupportedObjectClasses($_supportedObjectClasses)
{
$this->supportedOIDs = $_supportedObjectClasses;
$this->supportedObjectClasses = array_flip($_supportedObjectClasses);
}
/**
* sets the version
*
* @param integer $_version the supported ldap version
*/
function setVersion($_version)
{
$this->version = $_version;
}
/**
* checks for supported objectclasses
*
* @return bool returns true if the ldap server supports this objectclass
*/
function supportsObjectClass($_objectClass)
{
if($this->supportedObjectClasses[strtolower($_objectClass)])
{
return true;
}
return false;
}
/**
* Query given ldap connection for available information
*
* @param resource $ds
* @param string $host
* @param int $version 2 or 3
* @return ldapserverinfo
*/
public static function get($ds, $host, $version=3)
{
$filter='(objectclass=*)';
$justthese = array('structuralObjectClass','namingContexts','supportedLDAPVersion','subschemaSubentry','vendorname');
if(($sr = @ldap_read($ds, '', $filter, $justthese)))
{
if(($info = ldap_get_entries($ds, $sr)))
{
$ldapServerInfo = new ServerInfo($host);
$ldapServerInfo->setVersion($version);
// check for naming contexts
if($info[0]['namingcontexts'])
{
for($i=0; $i<$info[0]['namingcontexts']['count']; $i++)
{
$namingcontexts[] = $info[0]['namingcontexts'][$i];
}
$ldapServerInfo->setNamingContexts($namingcontexts);
}
// check for ldap server type
if($info[0]['structuralobjectclass'])
{
switch($info[0]['structuralobjectclass'][0])
{
case 'OpenLDAProotDSE':
$ldapServerType = OPENLDAP_LDAPSERVER;
break;
default:
$ldapServerType = UNKNOWN_LDAPSERVER;
break;
}
$ldapServerInfo->setServerType($ldapServerType);
}
if ($info[0]['vendorname'] && stripos($info[0]['vendorname'][0], 'samba') !== false)
{
$ldapServerInfo->setServerType(SAMBA4_LDAPSERVER);
}
// check for subschema entry dn
if($info[0]['subschemasubentry'])
{
$subschemasubentry = $info[0]['subschemasubentry'][0];
$ldapServerInfo->setSubSchemaEntry($subschemasubentry);
}
// create list of supported objetclasses
if(!empty($subschemasubentry))
{
$filter='(objectclass=*)';
$justthese = array('objectClasses');
if(($sr = ldap_read($ds, $subschemasubentry, $filter, $justthese)))
{
if(($info = ldap_get_entries($ds, $sr)))
{
if($info[0]['objectclasses']) {
for($i=0; $i<$info[0]['objectclasses']['count']; $i++)
{
$matches = null;
if(preg_match('/^\( (.*) NAME \'(\w*)\' /', $info[0]['objectclasses'][$i], $matches))
{
#_debug_array($matches);
if(count($matches) == 3)
{
$supportedObjectClasses[$matches[1]] = strtolower($matches[2]);
}
}
}
$ldapServerInfo->setSupportedObjectClasses($supportedObjectClasses);
}
}
}
}
}
}
return $ldapServerInfo;
}
}

View File

@ -167,7 +167,7 @@ class Storage extends Storage\Base
$this->extra_join_order = " LEFT JOIN $extra_table extra_order ON $table.$this->autoinc_id=extra_order.$this->extra_id";
$this->extra_join_filter = " JOIN $extra_table extra_filter ON $table.$this->autoinc_id=extra_filter.$this->extra_id";
$this->customfields = Customfields::get($app, false, null, $db);
$this->customfields = Storage\Customfields::get($app, false, null, $db);
}
/**

View File

@ -10,7 +10,9 @@
* @version $Id$
*/
namespace EGroupware\Api;
namespace EGroupware\Api\Storage;
use EGroupware\Api;
// explicitly reference classes still in phpgwapi
use common;
@ -28,7 +30,7 @@ class Customfields implements \IteratorAggregate
/**
* Reference to the global db class
*
* @var Db
* @var Api\Db
*/
static protected $db;
@ -94,13 +96,13 @@ class Customfields implements \IteratorAggregate
*/
function getIterator()
{
return new Db\CallbackIterator($this->iterator, function($_row)
return new Api\Db\CallbackIterator($this->iterator, function($_row)
{
$row = Db::strip_array_keys($_row, 'cf_');
$row = Api\Db::strip_array_keys($_row, 'cf_');
$row['private'] = $row['private'] ? explode(',', $row['private']) : array();
$row['type2'] = $row['type2'] ? explode(',', $row['type2']) : array();
$row['values'] = json_decode($row['values'], true);
$row['needed'] = Db::from_bool($row['needed']);
$row['needed'] = Api\Db::from_bool($row['needed']);
return $row;
}, array(), function($row)
@ -137,18 +139,18 @@ class Customfields implements \IteratorAggregate
public static function get($app, $all_private_too=false, $only_type2=null, egw_db $db=null)
{
$cache_key = $app.':'.($all_private_too?'all':$GLOBALS['egw_info']['user']['account_id']).':'.$only_type2;
$cfs = Cache::getInstance(__CLASS__, $cache_key);
$cfs = Api\Cache::getInstance(__CLASS__, $cache_key);
if (!isset($cfs))
{
$cfs = iterator_to_array(new Customfields($app, $all_private_too, $only_type2, 0, null, $db));
Cache::setInstance(__CLASS__, $cache_key, $cfs);
$cached = Cache::getInstance(__CLASS__, $app);
Api\Cache::setInstance(__CLASS__, $cache_key, $cfs);
$cached = Api\Cache::getInstance(__CLASS__, $app);
if (!in_array($cache_key, (array)$cached))
{
$cached[] = $cache_key;
Cache::setInstance(__CLASS__, $app, $cached);
Api\Cache::setInstance(__CLASS__, $app, $cached);
}
}
//error_log(__METHOD__."('$app', $all_private_too, '$only_type2') returning fields: ".implode(', ', array_keys($cfs)));
@ -224,7 +226,7 @@ class Customfields implements \IteratorAggregate
case 'date-time':
if ($value)
{
$value = DateTime::to($value, $field['type'] == 'date' ? true : '');
$value = Api\DateTime::to($value, $field['type'] == 'date' ? true : '');
}
break;
@ -461,13 +463,13 @@ class Customfields implements \IteratorAggregate
*/
protected static function invalidate_cache($app)
{
if (($cached = Cache::getInstance(__CLASS__, $app)))
if (($cached = Api\Cache::getInstance(__CLASS__, $app)))
{
foreach($cached as $key)
{
Cache::unsetInstance(__CLASS__, $key);
Api\Cache::unsetInstance(__CLASS__, $key);
}
Cache::unsetInstance(__CLASS__, $app);
Api\Cache::unsetInstance(__CLASS__, $app);
}
}
@ -532,7 +534,7 @@ class Customfields implements \IteratorAggregate
/**
* Initialise our db
*
* We use a reference here (no clone), as we no longer use Db::row() or Db::next_record()!
* We use a reference here (no clone), as we no longer use Api\Db::row() or Api\Db::next_record()!
*
*/
public static function init_static()

288
api/src/Storage/History.php Normal file
View File

@ -0,0 +1,288 @@
<?php
/**
* EGroupware API - Storage history logging
*
* @link http://www.egroupware.org
* @author Joseph Engo <jengo@phpgroupware.org>
* @copyright 2001 by Joseph Engo <jengo@phpgroupware.org>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> new DB-methods and search
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage storage
* @access public
* @version $Id$
*/
namespace EGroupware\Api\Storage;
use EGroupware\Api;
/**
* Record history logging service
*
* This class need to be instanciated for EACH app, which wishes to use it!
*/
class History
{
/**
* Reference to the global db object
*
* @var Api\Db
*/
var $db;
const TABLE = 'egw_history_log';
/**
* App.name this class is instanciated for / working on
*
* @var string
*/
var $appname;
var $user;
var $types = array(
'C' => 'Created',
'D' => 'Deleted',
'E' => 'Edited'
);
/**
* Constructor
*
* @param string $appname app name this instance operates on
* @return historylog
*/
function __construct($appname='',$user=null)
{
$this->appname = $appname ? $appname : $GLOBALS['egw_info']['flags']['currentapp'];
$this->user = !is_null($user) ? $user : $GLOBALS['egw_info']['user']['account_id'];
if (is_object($GLOBALS['egw_setup']->db))
{
$this->db = $GLOBALS['egw_setup']->db;
}
else
{
$this->db = $GLOBALS['egw']->db;
}
}
/**
* Delete the history-log of one or multiple records of $this->appname
*
* @param int|array $record_id one or more id's of $this->appname, or null to delete ALL records of $this->appname
* @return int number of deleted records/rows (0 is not necessaryly an error, it can just mean there's no record!)
*/
function delete($record_id)
{
$where = array('history_appname' => $this->appname);
if (is_array($record_id) || is_numeric($record_id))
{
$where['history_record_id'] = $record_id;
}
$this->db->delete(self::TABLE,$where,__LINE__,__FILE__);
return $this->db->affected_rows();
}
/**
* Add a history record, if $new_value != $old_value
*
* @param string $status 2 letter code: eg. $this->types: C=Created, D=Deleted, E=Edited
* @param int $record_id it of the record in $this->appname (set by the constructor)
* @param string $new_value new value
* @param string $old_value old value
*/
function add($status,$record_id,$new_value,$old_value)
{
if ($new_value != $old_value)
{
$this->db->insert(self::TABLE,array(
'history_record_id' => $record_id,
'history_appname' => $this->appname,
'history_owner' => $this->user,
'history_status' => $status,
'history_new_value' => $new_value,
'history_old_value' => $old_value,
'history_timestamp' => time(),
'sessionid' => $GLOBALS['egw']->session->sessionid_access_log,
),false,__LINE__,__FILE__);
}
}
/**
* Static function to add a history record
*/
public static function static_add($appname, $id, $user, $field_code, $new_value, $old_value = '')
{
if ($new_value != $old_value)
{
$GLOBALS['egw']->db->insert(self::TABLE,array(
'history_record_id' => $id,
'history_appname' => $appname,
'history_owner' => (int)$user,
'history_status' => $field_code,
'history_new_value' => $new_value,
'history_old_value' => $old_value,
'history_timestamp' => time(),
'sessionid' => $GLOBALS['egw']->session->sessionid_access_log,
),false,__LINE__,__FILE__);
}
}
/**
* Search history-log
*
* @param array|int $filter array with filters, or int record_id
* @param string $order ='history_id' sorting after history_id is identical to history_timestamp
* @param string $sort ='DESC'
* @param int $limit =null only return this many entries
* @return array of arrays with keys id, record_id, appname, owner (account_id), status, new_value, old_value,
* timestamp (Y-m-d H:i:s in servertime), user_ts (timestamp in user-time)
*/
function search($filter,$order='history_id',$sort='DESC',$limit=null)
{
if (!is_array($filter)) $filter = is_numeric($filter) ? array('history_record_id' => $filter) : array();
if (!$order || !preg_match('/^[a-z0-9_]+$/i',$order) || !preg_match('/^(asc|desc)?$/i',$sort))
{
$orderby = 'ORDER BY history_id DESC';
}
else
{
$orderby = "ORDER BY $order $sort";
}
foreach($filter as $col => $value)
{
if (!is_numeric($col) && substr($col,0,8) != 'history_')
{
$filter['history_'.$col] = $value;
unset($filter[$col]);
}
}
if (!isset($filter['history_appname'])) $filter['history_appname'] = $this->appname;
// do not try to read all history entries of an app
if (!$filter['history_record_id']) return array();
$rows = array();
foreach($this->db->select(self::TABLE, '*', $filter, __LINE__, __FILE__,
isset($limit) ? 0 : false, $orderby, 'phpgwapi', $limit) as $row)
{
$row['user_ts'] = $this->db->from_timestamp($row['history_timestamp']) + 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'];
$rows[] = Api\Db::strip_array_keys($row,'history_');
}
return $rows;
}
/**
* Get a slice of history records
*
* Similar to search(), except this one can take a start and a number of records
*
* @see Base::get_rows()
*/
public static function get_rows(&$query, &$rows)
{
$filter = array();
$rows = array();
$filter['history_appname'] = $query['appname'];
$filter['history_record_id'] = $query['record_id'];
if(is_array($query['colfilter'])) {
foreach($query['colfilter'] as $column => $value) {
$filter[$column] = $value;
}
}
if ($GLOBALS['egw']->db->Type == 'mysql' && $GLOBALS['egw']->db->ServerInfo['version'] >= 4.0)
{
$mysql_calc_rows = 'SQL_CALC_FOUND_ROWS ';
}
else
{
$total = $GLOBALS['egw']->db->select(self::TABLE,'COUNT(*)',$filter,__LINE__,__FILE__,false,'','phpgwapi',0)->fetchColumn();
}
// filter out private (or no longer defined) custom fields
if ($filter['history_appname'])
{
$to_or[] = "history_status NOT LIKE '#%'";
// explicitly allow "##" used to store iCal/vCard X-attributes
if (in_array($filter['history_appname'], array('calendar','infolog','addressbook')))
{
$to_or[] = "history_status LIKE '##%'";
}
if (($cfs = Customfields::get($filter['history_appname'])))
{
$to_or[] = 'history_status IN ('.implode(',', array_map(function($str)
{
return $GLOBALS['egw']->db->quote('#'.$str);
}, array_keys($cfs))).')';
}
$filter[] = '('.implode(' OR ', $to_or).')';
}
$_query = array(array(
'table' => self::TABLE,
'cols' => array('history_id', 'history_record_id','history_appname','history_owner','history_status','history_new_value', 'history_timestamp','history_old_value'),
'where' => $filter,
));
// Add in files, if possible
if($GLOBALS['egw_info']['user']['apps']['filemanager'] &&
$file = Api\Vfs\Sqlfs\StreamWrapper::url_stat("/apps/{$query['appname']}/{$query['record_id']}",STREAM_URL_STAT_LINK))
{
$_query[] = array(
'table' => Api\Vfs\Sqlfs\StreamWrapper::TABLE,
'cols' =>array('fs_id', 'fs_dir', "'filemanager'",'COALESCE(fs_modifier,fs_creator)',"'~file~'",'fs_name','fs_modified', 'fs_mime'),
'where' => array('fs_dir' => $file['ino'])
);
}
$new_file_id = array();
foreach($GLOBALS['egw']->db->union(
$_query,
__LINE__, __FILE__,
' ORDER BY ' . ($query['order'] ? $query['order'] : 'history_timestamp') . ' ' . ($query['sort'] ? $query['sort'] : 'DESC'),
$query['start'],
$query['num_rows']
) as $row)
{
$row['user_ts'] = $GLOBALS['egw']->db->from_timestamp($row['history_timestamp']) + 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset'];
// Explode multi-part values
foreach(array('history_new_value','history_old_value') as $field)
{
if(strpos($row[$field],Tracking::ONE2N_SEPERATOR) !== false)
{
$row[$field] = explode(Tracking::ONE2N_SEPERATOR,$row[$field]);
}
}
// Get information needed for proper display
if($row['history_appname'] == 'filemanager')
{
$new_version = $new_file_id[$row['history_new_value']];
$new_file_id[$row['history_new_value']] = count($rows);
$path = Api\Vfs\Sqlfs\StreamWrapper::id2path($row['history_id']);
// Apparently we don't have to do anything with it, just ask...
// without this, previous versions are not handled properly
Api\Vfs::getExtraInfo($path);
$row['history_new_value'] = array(
'path' => $path,
'name' => Api\Vfs::basename($path),
'mime' => $row['history_old_value']
);
$row['history_old_value'] = '';
if($new_version !== null)
{
$rows[$new_version]['old_value'] = $row['history_new_value'];
}
}
$rows[] = Api\Db::strip_array_keys($row,'history_');
}
if ($mysql_calc_rows)
{
$total = $GLOBALS['egw']->db->query('SELECT FOUND_ROWS()')->fetchColumn();
}
return $total;
}
}

1220
api/src/Storage/Tracking.php Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,11 +5,13 @@
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package setup
* @copyright (c) 2007-13 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
use EGroupware\Api;
/**
* setup command: test or create the ldap connection and hierarchy
*
@ -121,7 +123,7 @@ class setup_cmd_ldap extends setup_cmd
{
if (!empty($this->domain) && !preg_match('/^([a-z0-9_-]+\.)*[a-z0-9]+/i',$this->domain))
{
throw new egw_exception_wrong_userinput(lang("'%1' is no valid domain name!",$this->domain));
throw new Api\Exception\WrongUserinput(lang("'%1' is no valid domain name!",$this->domain));
}
if ($this->remote_id && $check_only && !in_array($this->sub_command, array('set_mailbox', 'sid2uidnumber', 'copy2ad')))
{
@ -186,7 +188,7 @@ class setup_cmd_ldap extends setup_cmd
// check if base does exist
if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*'))
{
throw new egw_exception_wrong_userinput(lang('Base dn "%1" NOT found!',$this->ldap_base));
throw new Api\Exception\WrongUserinput(lang('Base dn "%1" NOT found!',$this->ldap_base));
}
if (!($sr = ldap_search($this->test_ldap->ds,$this->ldap_base,
@ -194,7 +196,7 @@ class setup_cmd_ldap extends setup_cmd
array('uidNumber','gidNumber','uid','cn', 'objectClass',self::sambaSID))) ||
!($entries = ldap_get_entries($this->test_ldap->ds, $sr)))
{
throw new egw_exception(lang('Error searching "dn=%1" for "%2"!',$this->ldap_base, $search));
throw new Api\Exception(lang('Error searching "dn=%1" for "%2"!',$this->ldap_base, $search));
}
$change = $accounts = array();
$cmd_change_account_id = 'admin/admin-cli.php --change-account-id <admin>@<domain>,<adminpw>';
@ -203,7 +205,7 @@ class setup_cmd_ldap extends setup_cmd
{
if ($key === 'count') continue;
$entry = ldap::result2array($entry);
$entry = Api\Ldap::result2array($entry);
$accounts[$entry['dn']] = $entry;
//print_r($entry);
@ -247,7 +249,7 @@ class setup_cmd_ldap extends setup_cmd
}
if (!$check_only && $modify && !ldap_modify($this->test_ldap->ds, $dn, $modify))
{
throw new egw_exception("Failed to modify ldap: !ldap_modify({$this->test_ldap->ds}, '$dn', ".array2string($modify).") ".ldap_error($this->test_ldap->ds).
throw new Api\Exception("Failed to modify ldap: !ldap_modify({$this->test_ldap->ds}, '$dn', ".array2string($modify).") ".ldap_error($this->test_ldap->ds).
"\n- ".implode("\n- ", $msg)); // EGroupware change already run successful
}
if ($modify) ++$changed;
@ -313,7 +315,7 @@ class setup_cmd_ldap extends setup_cmd
// check if ads base does exist
if (!@ldap_read($ads->ds, $this->ads_context, 'objectClass=*'))
{
throw new egw_exception_wrong_userinput(lang('Ads dn "%1" NOT found!',$this->ads_context));
throw new Api\Exception\WrongUserInput(lang('Ads dn "%1" NOT found!',$this->ads_context));
}
// connect to source ldap
@ -322,7 +324,7 @@ class setup_cmd_ldap extends setup_cmd
// check if ldap base does exist
if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*'))
{
throw new egw_exception_wrong_userinput(lang('Base dn "%1" NOT found!',$this->ldap_base));
throw new Api\Exception\WrongUserInput(lang('Base dn "%1" NOT found!',$this->ldap_base));
}
if (!($sr = ldap_search($this->test_ldap->ds,$this->ldap_base,
@ -330,7 +332,7 @@ class setup_cmd_ldap extends setup_cmd
'(&(objectClass=posixAccount)('.self::sambaSID.'=*)(!(uid=*$)))', $attrs)) ||
!($entries = ldap_get_entries($this->test_ldap->ds, $sr)))
{
throw new egw_exception(lang('Error searching "dn=%1" for "%2"!',$this->ldap_base, $search));
throw new Api\Exception(lang('Error searching "dn=%1" for "%2"!',$this->ldap_base, $search));
}
$changed = 0;
$utc_diff = null;
@ -338,12 +340,12 @@ class setup_cmd_ldap extends setup_cmd
{
if ($key === 'count') continue;
$entry_arr = ldap::result2array($entry);
$entry_arr = Api\Ldap::result2array($entry);
$uid = $entry_arr['uid'];
$entry = array_diff_key($entry_arr, $ignore_attr);
if (!($sr = ldap_search($ads->ds, $this->ads_context,
$search='(&(objectClass=user)(sAMAccountName='.ldap::quote($uid).'))', array('dn'))) ||
$search='(&(objectClass=user)(sAMAccountName='.Api\Ldap::quote($uid).'))', array('dn'))) ||
!($dest = ldap_get_entries($ads->ds, $sr)))
{
$msg[] = lang('User "%1" not found!', $uid);
@ -591,7 +593,7 @@ class setup_cmd_ldap extends setup_cmd
// should we run any or some addAccount hooks
if ($this->add_account_hook)
{
// setting up egw_info array with new ldap information, so hook can use ldap::ldapConnect()
// setting up egw_info array with new ldap information, so hook can use Api\Ldap::ldapConnect()
if (!$egw_info_set++)
{
foreach(array('ldap_host','ldap_root_dn','ldap_root_pw','ldap_context','ldap_group_context','ldap_search_filter','ldap_encryptin_type','mail_suffix','mail_login_type') as $name)
@ -798,7 +800,7 @@ class setup_cmd_ldap extends setup_cmd
* @param string $dn =null default $this->ldap_root_dn
* @param string $pw =null default $this->ldap_root_pw
* @param string $host =null default $this->ldap_host, hostname, ip or ldap-url
* @throws egw_exception_wrong_userinput Can not connect to ldap ...
* @throws Api\Exception\WrongUserInput Can not connect to ldap ...
*/
private function connect($dn=null,$pw=null,$host=null)
{
@ -806,9 +808,9 @@ class setup_cmd_ldap extends setup_cmd
if (is_null($pw)) $pw = $this->ldap_root_pw;
if (is_null($host)) $host = $this->ldap_host;
if (!$pw) // ldap::ldapConnect use the current eGW's pw otherwise
if (!$pw) // Api\Ldap::ldapConnect use the current eGW's pw otherwise
{
throw new egw_exception_wrong_userinput(lang('You need to specify a password!'));
throw new Api\Exception\WrongUserInput(lang('You need to specify a password!'));
}
$this->test_ldap = new ldap();
@ -821,7 +823,7 @@ class setup_cmd_ldap extends setup_cmd
if (!$ds)
{
throw new egw_exception_wrong_userinput(lang('Can not connect to LDAP server on host %1 using DN %2!',
throw new Api\Exception\WrongUserInput(lang('Can not connect to LDAP server on host %1 using DN %2!',
$host,$dn).($this->test_ldap->ds ? ' ('.ldap_error($this->test_ldap->ds).')' : ''));
}
return lang('Successful connected to LDAP server on %1 using DN %2.',$this->ldap_host,$dn);
@ -831,7 +833,7 @@ class setup_cmd_ldap extends setup_cmd
* Count active (not expired) users
*
* @return int number of active users
* @throws egw_exception_wrong_userinput
* @throws Api\Exception\WrongUserInput
*/
private function users()
{
@ -840,7 +842,7 @@ class setup_cmd_ldap extends setup_cmd
$sr = ldap_list($this->test_ldap->ds,$this->ldap_context,'ObjectClass=posixAccount',array('dn','shadowExpire'));
if (!($entries = ldap_get_entries($this->test_ldap->ds, $sr)))
{
throw new egw_exception('Error listing "dn=%1"!',$this->ldap_context);
throw new Api\Exception('Error listing "dn=%1"!',$this->ldap_context);
}
$num = 0;
foreach($entries as $n => $entry)
@ -856,7 +858,7 @@ class setup_cmd_ldap extends setup_cmd
* Check and if does not yet exist create the new database and user
*
* @return string with success message
* @throws egw_exception_wrong_userinput
* @throws Api\Exception\WrongUserInput
*/
private function create()
{
@ -883,7 +885,7 @@ class setup_cmd_ldap extends setup_cmd
* Delete whole LDAP tree of an instance dn=$this->ldap_base using $this->ldap_admin/_pw
*
* @return string with success message
* @throws egw_exception if dn not found, not listable or delete fails
* @throws Api\Exception if dn not found, not listable or delete fails
*/
private function delete_base()
{
@ -897,12 +899,12 @@ class setup_cmd_ldap extends setup_cmd
// some precausion to not delete whole ldap tree!
if (count(explode(',',$this->ldap_base)) < 2)
{
throw new egw_exception_assertion_failed(lang('Refusing to delete dn "%1"!',$this->ldap_base));
throw new Api\Exception\AssertionFailed(lang('Refusing to delete dn "%1"!',$this->ldap_base));
}
// check if base does exist
if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*'))
{
throw new egw_exception_wrong_userinput(lang('Base dn "%1" NOT found!',$this->ldap_base));
throw new Api\Exception\WrongUserInput(lang('Base dn "%1" NOT found!',$this->ldap_base));
}
return lang('LDAP dn="%1" with %2 entries deleted.',
$this->ldap_base,$this->rdelete($this->ldap_base));
@ -913,14 +915,14 @@ class setup_cmd_ldap extends setup_cmd
*
* @param string $dn
* @return int integer number of deleted entries
* @throws egw_exception if dn not listable or delete fails
* @throws Api\Exception if dn not listable or delete fails
*/
private function rdelete($dn)
{
if (!($sr = ldap_list($this->test_ldap->ds,$dn,'ObjectClass=*',array(''))) ||
!($entries = ldap_get_entries($this->test_ldap->ds, $sr)))
{
throw new egw_exception(lang('Error listing "dn=%1"!',$dn));
throw new Api\Exception(lang('Error listing "dn=%1"!',$dn));
}
$deleted = 0;
foreach($entries as $n => $entry)
@ -930,7 +932,7 @@ class setup_cmd_ldap extends setup_cmd
}
if (!ldap_delete($this->test_ldap->ds,$dn))
{
throw new egw_exception(lang('Error deleting "dn=%1"!',$dn));
throw new Api\Exception(lang('Error deleting "dn=%1"!',$dn));
}
return ++$deleted;
}
@ -944,7 +946,7 @@ class setup_cmd_ldap extends setup_cmd
* @param string $this->mbox_attr ='mailmessagestore' lowercase!!!
* @param string $this->mail_login_type ='email' 'email', 'vmailmgr', 'standard' or 'uidNumber'
* @return string with success message N entries modified
* @throws egw_exception if dn not found, not listable or delete fails
* @throws Api\Exception if dn not found, not listable or delete fails
*/
private function set_mailbox($check_only=false)
{
@ -958,7 +960,7 @@ class setup_cmd_ldap extends setup_cmd
// check if base does exist
if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*'))
{
throw new egw_exception_wrong_userinput(lang('Base dn "%1" NOT found!',$this->ldap_base));
throw new Api\Exception\WrongUserInput(lang('Base dn "%1" NOT found!',$this->ldap_base));
}
$object_class = $this->object_class ? $this->object_class : 'qmailUser';
$mbox_attr = $this->mbox_attr ? $this->mbox_attr : 'mailmessagestore';
@ -968,7 +970,7 @@ class setup_cmd_ldap extends setup_cmd
'objectClass='.$object_class,array('mail','uidNumber','uid',$mbox_attr))) ||
!($entries = ldap_get_entries($this->test_ldap->ds, $sr)))
{
throw new egw_exception(lang('Error listing "dn=%1"!',$this->ldap_base));
throw new Api\Exception(lang('Error listing "dn=%1"!',$this->ldap_base));
}
$modified = 0;
foreach($entries as $n => $entry)
@ -987,7 +989,7 @@ class setup_cmd_ldap extends setup_cmd
$mbox_attr => $mbox,
)))
{
throw new egw_exception(lang("Error modifying dn=%1: %2='%3'!",$entry['dn'],$mbox_attr,$mbox));
throw new Api\Exception(lang("Error modifying dn=%1: %2='%3'!",$entry['dn'],$mbox_attr,$mbox));
}
++$modified;
if ($check_only) echo "$modified: $entry[dn]: $mbox_attr={$entry[$mbox_attr][0]} --> $mbox\n";
@ -1015,7 +1017,7 @@ class setup_cmd_ldap extends setup_cmd
* @param string $dn dn to create, eg. "cn=admin,dc=local"
* @param array $extra =array() extra attributes to set
* @return boolean true if the node was create, false if it was already there
* @throws egw_exception_wrong_userinput
* @throws Api\Exception\WrongUserinput
*/
private function _create_node($dn,$extra=array())
{
@ -1035,7 +1037,7 @@ class setup_cmd_ldap extends setup_cmd
if (!isset(self::$requiredObjectclasses[$name]))
{
throw new egw_exception_wrong_userinput(lang('Can not create DN %1!',$dn).' '.
throw new Api\Exception\WrongUserinput(lang('Can not create DN %1!',$dn).' '.
lang('Supported node types:').implode(', ',array_keys(self::$requiredObjectclasses)));
}
if ($name == 'dc') $extra['o'] = $value; // required by organisation
@ -1046,7 +1048,7 @@ class setup_cmd_ldap extends setup_cmd
'objectClass' => self::$requiredObjectclasses[$name],
)+$extra))
{
throw new egw_exception_wrong_userinput(lang('Can not create DN %1!',$dn).
throw new Api\Exception\WrongUserinput(lang('Can not create DN %1!',$dn).
' ('.ldap_error($this->test_ldap->ds).', attributes='.print_r($attr,true).')');
}
return true;