diff --git a/api/src/Accounts.php b/api/src/Accounts.php new file mode 100644 index 0000000000..a95a74ab12 --- /dev/null +++ b/api/src/Accounts.php @@ -0,0 +1,1193 @@ + complete rewrite in 6/2006 and earlier modifications + * + * Implements the (now depricated) interfaces on the former accounts class written by + * Joseph Engo and Bettina Gille + * Copyright (C) 2000 - 2002 Joseph Engo, Copyright (C) 2003 Joseph Engo, Bettina Gille + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @access public + * @version $Id$ + */ + +namespace EGroupware\Api; + +// explicitly reference classes still in phpgwapi of old structure +use egw; + +/** + * API - accounts + * + * This class uses a backend class (at them moment SQL or LDAP) and implements some + * caching on to top of the backend functions: + * + * a) instance-wide account-data cache queried by account_id including also members(hips) + * implemented by self::cache_read($account_id) and self::cache_invalidate($account_ids) + * + * b) session based cache for search, split_accounts and name2id + * implemented by self::setup_cache() and self::cache_invalidate() + * + * The backend only implements the read, save, delete, name2id and the {set_}members{hips} methods. + * The account class implements all other (eg. name2id, id2name) functions on top of these. + * + * read and search return timestamps (account_(created|modified|lastlogin) in server-time! + */ +class Accounts +{ + /** + * Enables the session-cache, currently switched on independent of the backend + * + * @var boolean + */ + static $use_session_cache = true; + + /** + * Cache, stored in sesssion + * + * @var array + */ + static $cache; + + /** + * Keys for which both versions with 'account_' prefix and without (depricated!) can be used, if requested. + * Migrate your code to always use the 'account_' prefix!!! + * + * @var array + */ + var $depricated_names = array('firstname','lastname','fullname','email','type', + 'status','expires','lastlogin','lastloginfrom','lastpasswd_change'); + + /** + * List of all config vars accounts depend on and therefore should be passed in when calling contructor with array syntax + * + * @var array + */ + static public $config_vars = array( + 'account_repository', 'auth_type', // auth_type if fallback if account_repository is not set + 'install_id', // instance-specific caching + 'auto_create_expire', 'default_group_lid', // auto-creation of accounts + 'ldap_host','ldap_root_dn','ldap_root_pw','ldap_context','ldap_group_context','ldap_search_filter', // ldap backend + 'ads_domain', 'ads_host', 'ads_admin_user', 'ads_admin_passwd', 'ads_connection', 'ads_context', // ads backend + ); + + /** + * Querytypes for the account-search + * + * @var array + */ + var $query_types = array( + 'all' => 'all fields', + 'firstname' => 'firstname', + 'lastname' => 'lastname', + 'lid' => 'LoginID', + 'email' => 'email', + 'start' => 'start with', + 'exact' => 'exact', + ); + + /** + * Backend to use + * + * @var Accounts\Sql|Accounts\Ldap|Accounts\Ads|Accounts\Univention + */ + var $backend; + + /** + * total number of found entries + * + * @var int + */ + var $total; + + /** + * Current configuration + * + * @var array + */ + var $config; + + /** + * hold an instance of the accounts class + * + * @var Accounts the instance of the accounts class + */ + private static $_instance = NULL; + + /** + * Singleton + * + * @return Accounts + */ + public static function getInstance() + { + if (self::$_instance === NULL) + { + self::$_instance = new Accounts(); + } + return self::$_instance; + } + + /** + * Constructor + * + * @param string|array $backend =null string with backend 'sql'|'ldap', or whole config array, default read from global egw_info + */ + public function __construct($backend=null) + { + if (is_array($backend)) + { + $this->config = $backend; + $backend = null; + self::$_instance = $this; // also set instance returned by singleton + self::$cache = array(); // and empty our internal (session) cache + } + else + { + $this->config =& $GLOBALS['egw_info']['server']; + + if (!isset(self::$_instance)) self::$_instance = $this; + } + if (is_null($backend)) + { + if (empty($this->config['account_repository'])) + { + if (!empty($this->config['auth_type'])) + { + $this->config['account_repository'] = $this->config['auth_type']; + } + else + { + $this->config['account_repository'] = 'sql'; + } + } + $backend = $this->config['account_repository']; + } + $backend_class = 'EGroupware\\Api\\Accounts\\'.ucfirst($backend); + + $this->backend = new $backend_class($this); + } + + /** + * Searches / lists accounts: users and/or groups + * + * @param array with the following keys: + * @param $param['type'] string|int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both', + * 'groupmembers' (members of groups the user is a member of), 'groupmembers+memberships' (incl. memberships too) + * or integer group-id for a list of members of that group + * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set + * @param $param['order'] string column to sort after, default account_lid if unset + * @param $param['sort'] string 'ASC' or 'DESC', default 'ASC' if not set + * @param $param['query'] string to search for, no search if unset or empty + * @param $param['query_type'] string: + * 'all' - query all fields for containing $param[query] + * 'start' - query all fields starting with $param[query] + * 'exact' - query all fields for exact $param[query] + * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] + * @param $param['app'] string with an app-name, to limit result on accounts with run-right for that app + * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs + * @param $param['active']=true boolean - true: return only acctive accounts, false: return expired or deactivated too + * @return array with account_id => data pairs, data is an array with account_id, account_lid, account_firstname, + * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group + */ + function search($param) + { + //error_log(__METHOD__.'('.array2string($param).') '.function_backtrace()); + if (!isset($param['active'])) $param['active'] = true; // default is true = only return active accounts + + self::setup_cache(); + $account_search = &self::$cache['account_search']; + $serial = serialize($param); + + if (isset($account_search[$serial])) + { + $this->total = $account_search[$serial]['total']; + } + // no backend understands $param['app'], only sql understands type owngroups or groupmemember[+memberships] + // --> do an full search first and then filter and limit that search + elseif($param['app'] || $this->config['account_repository'] != 'sql' && + in_array($param['type'], array('owngroups','groupmembers','groupmembers+memberships'))) + { + $app = $param['app']; + unset($param['app']); + $start = $param['start']; + unset($param['start']); + $offset = $param['offset'] ? $param['offset'] : $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; + unset($param['offset']); + $stop = $start + $offset; + + if ($param['type'] == 'owngroups') + { + $members = $this->memberships($GLOBALS['egw_info']['user']['account_id'],true); + $param['type'] = 'groups'; + } + elseif(in_array($param['type'],array('groupmembers','groupmembers+memberships'))) + { + $members = array(); + foreach((array)$this->memberships($GLOBALS['egw_info']['user']['account_id'],true) as $grp) + { + $members = array_unique(array_merge($members, (array)$this->members($grp,true,$param['active']))); + if ($param['type'] == 'groupmembers+memberships') $members[] = $grp; + } + $param['type'] = $param['type'] == 'groupmembers+memberships' ? 'both' : 'accounts'; + } + // call ourself recursive to get (evtl. cached) full search + $full_search = $this->search($param); + + // filter search now on accounts with run-rights for app or a group + $valid = array(); + if ($app) + { + // we want the result merged, whatever it takes, as we only care for the ids + $valid = $this->split_accounts($app,!in_array($param['type'],array('accounts','groups')) ? 'merge' : $param['type'],$param['active']); + } + if (isset($members)) + { + //error_log(__METHOD__.'() members='.array2string($members)); + if (!$members) $members = array(); + $valid = !$app ? $members : array_intersect($valid,$members); // use the intersection + } + //error_log(__METHOD__."() limiting result to app='$app' and/or group=$group valid-ids=".array2string($valid)); + $n = 0; + $account_search[$serial]['data'] = array(); + foreach ($full_search as $id => $data) + { + if (!in_array($id,$valid)) + { + $this->total--; + continue; + } + // now we have a valid entry + if (!is_int($start) || $start <= $n && $n < $stop) + { + $account_search[$serial]['data'][$id] = $data; + } + $n++; + } + $account_search[$serial]['total'] = $this->total; + } + // direct search via backend + else + { + $account_search[$serial]['data'] = $this->backend->search($param); + if ($param['type'] !== 'accounts') + { + foreach($account_search[$serial]['data'] as &$account) + { + // add default description for Admins and Default group + if ($account['account_type'] === 'g' && empty($account['account_description'])) + { + self::add_default_group_description($account); + } + } + } + $account_search[$serial]['total'] = $this->total = $this->backend->total; + } + return $account_search[$serial]['data']; + } + + /** + * Query for accounts + * + * @param string|array $pattern + * @param array $options + * $options['filter']['group'] only return members of that group + * $options['account_type'] "accounts", "groups", "both" or "groupmembers" + * @return array with id - title pairs of the matching entries + */ + public static function link_query($pattern, array &$options = array()) + { + if (isset($options['filter']) && !is_array($options['filter'])) + { + $options['filter'] = (array)$options['filter']; + } + switch($GLOBALS['egw_info']['user']['preferences']['common']['account_display']) + { + case 'firstname': + case 'firstall': + $order = 'account_firstname,account_lastname'; + break; + case 'lastname': + case 'lastall': + $order = 'account_lastname,account_firstname'; + break; + default: + $order = 'account_lid'; + break; + } + $only_own = $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] === 'groupmembers' && + !isset($GLOBALS['egw_info']['user']['apps']['admin']); + switch($options['account_type']) + { + case 'accounts': + $type = $only_own ? 'groupmembers' : 'accounts'; + break; + case 'groups': + $type = $only_own ? 'owngroups' : 'groups'; + break; + case 'memberships': + $type = 'owngroups'; + break; + case 'owngroups': + case 'groupmembers': + $type = $options['account_type']; + break; + case 'both': + default: + $type = $only_own ? 'groupmembers+memberships' : 'both'; + break; + } + $accounts = array(); + foreach(self::getInstance()->search(array( + 'type' => $options['filter']['group'] < 0 ? $options['filter']['group'] : $type, + 'query' => $pattern, + 'query_type' => 'all', + 'order' => $order, + )) as $account) + { + $accounts[$account['account_id']] = self::format_username($account['account_lid'], + $account['account_firstname'],$account['account_lastname'],$account['account_id']); + } + return $accounts; + } + + /** + * Reads the data of one account + * + * It's depricated to use read with out parameter to read the internal data of this class!!! + * All key of the returned array use the 'account_' prefix. + * For backward compatibility some values are additionaly availible without the prefix, using them is depricated! + * + * @param int|string $id numeric account_id or string with account_lid (use of default value of 0 is depricated!!!) + * @param boolean $set_depricated_names =false set _additionaly_ the depricated keys without 'account_' prefix + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found + */ + function read($id=0,$set_depricated_names=false) + { + if (!$id) // deprecated use!!! + { + return $this->data ? $this->data : $this->read_repository(); + } + if (!is_int($id) && !is_numeric($id)) + { + $id = $this->name2id($id); + } + if (!$id) return false; + + $data = self::cache_read($id); + + // add default description for Admins and Default group + if ($data['account_type'] === 'g' && empty($data['account_description'])) + { + self::add_default_group_description($data); + } + + if ($set_depricated_names && $data) + { + foreach($this->depricated_names as $name) + { + $data[$name] =& $data['account_'.$name]; + } + } + return $data; + } + + /** + * Get an account as json, returns only whitelisted fields: + * - 'account_id','account_lid','person_id','account_status', + * - 'account_firstname','account_lastname','account_email','account_fullname','account_phone' + * + * @param int|string $id + * @return string|boolean json or false if not found + */ + function json($id) + { + static $keys = array( + 'account_id','account_lid','person_id','account_status', + 'account_firstname','account_lastname','account_email','account_fullname','account_phone', + ); + if (($account = $this->read($id))) + { + $account = array_intersect_key($account, array_flip($keys)); + } + // for current user, add the apps available to him + if ($id == $GLOBALS['egw_info']['user']['account_id']) + { + foreach((array)$GLOBALS['egw_info']['user']['apps'] as $app => $data) + { + unset($data['table_defs']); // no need for that on the client + $account['apps'][$app] = $data; + } + } + return json_encode($account); + } + + /** + * Format lid, firstname, lastname according to use preferences + * + * @param $lid ='' account loginid + * @param $firstname ='' firstname + * @param $lastname ='' lastname + * @param $accountid =0 id, to check if it's a user or group, otherwise the lid will be used + */ + static function format_username($lid = '', $firstname = '', $lastname = '', $accountid=0) + { + if (!$lid && !$firstname && !$lastname) + { + $lid = $GLOBALS['egw_info']['user']['account_lid']; + $firstname = $GLOBALS['egw_info']['user']['account_firstname']; + $lastname = $GLOBALS['egw_info']['user']['account_lastname']; + } + $is_group = $GLOBALS['egw']->accounts->get_type($accountid ? $accountid : $lid) == 'g'; + + if (empty($firstname)) $firstname = $lid; + if (empty($lastname) || $is_group) + { + $lastname = $is_group ? lang('Group') : lang('User'); + } + $display = $GLOBALS['egw_info']['user']['preferences']['common']['account_display']; + + if ($firstname && $lastname) + { + $delimiter = $is_group ? ' ' : ', '; + } + else + { + $delimiter = ''; + } + + $name = ''; + switch($display) + { + case 'firstname': + $name = $firstname . ' ' . $lastname; + break; + case 'lastname': + $name = $lastname . $delimiter . $firstname; + break; + case 'username': + $name = $lid; + break; + case 'firstall': + $name = $firstname . ' ' . $lastname . ' ['.$lid.']'; + break; + case 'lastall': + $name = $lastname . $delimiter . $firstname . ' ['.$lid.']'; + break; + case 'allfirst': + $name = '['.$lid.'] ' . $firstname . ' ' . $lastname; + break; + case 'all': + /* fall through */ + default: + $name = '['.$lid.'] ' . $lastname . $delimiter . $firstname; + } + return $name; + } + + /** + * Return formatted username for a given account_id + * + * @param string $account_id =null account id + * @return string full name of user or "#$accountid" if user not found + */ + static function username($account_id=null) + { + if ($account_id && !($account = self::cache_read($account_id))) + { + return '#'.$account_id; + } + return self::format_username($account['account_lid'], + $account['account_firstname'] , $account['account_lastname'], $account_id); + } + + /** + * Add a default description for stock groups: Admins, Default, NoGroup + * + * @param array &$data + */ + protected static function add_default_group_description(array &$data) + { + switch($data['account_lid']) + { + case 'Default': + $data['account_description'] = lang('EGroupware all users group, do NOT delete'); + break; + case 'Admins': + $data['account_description'] = lang('EGroupware administrators group, do NOT delete'); + break; + case 'NoGroup': + $data['account_description'] = lang('EGroupware anonymous users group, do NOT delete'); + break; + } + } + + /** + * Saves / adds the data of one account + * + * If no account_id is set in data the account is added and the new id is set in $data. + * + * @param array $data array with account-data + * @param boolean $check_depricated_names =false check _additionaly_ the depricated keys without 'account_' prefix + * @return int|boolean the account_id or false on error + */ + function save(&$data,$check_depricated_names=false) + { + if ($check_depricated_names) + { + foreach($this->depricated_names as $name) + { + if (isset($data[$name]) && !isset($data['account_'.$name])) + { + $data['account_'.$name] =& $data[$name]; + } + } + } + // add default description for Admins and Default group + if ($data['account_type'] === 'g' && empty($data['account_description'])) + { + self::add_default_group_description($data); + } + if (($id = $this->backend->save($data)) && $data['account_type'] != 'g') + { + // if we are not on a pure LDAP system, we have to write the account-date via the contacts class now + if (($this->config['account_repository'] == 'sql' || $this->config['contact_repository'] == 'sql-ldap') && + (!($old = $this->read($data['account_id'])) || // only for new account or changed contact-data + $old['account_firstname'] != $data['account_firstname'] || + $old['account_lastname'] != $data['account_lastname'] || + $old['account_email'] != $data['account_email'])) + { + if (!$data['person_id']) $data['person_id'] = $old['person_id']; + + $contact = array( + 'n_given' => $data['account_firstname'], + 'n_family' => $data['account_lastname'], + 'email' => $data['account_email'], + 'account_id' => $data['account_id'], + 'id' => $data['person_id'], + 'owner' => 0, + ); + $GLOBALS['egw']->contacts->save($contact,true); // true = ignore addressbook acl + } + // save primary group if necessary + if ($data['account_primary_group'] && (!($memberships = $this->memberships($id,true)) || + !in_array($data['account_primary_group'],$memberships))) + { + $memberships[] = $data['account_primary_group']; + $this->set_memberships($memberships, $id); // invalidates cache for account_id and primary group + } + } + // as some backends set (group-)members in save, we need to invalidate their members too! + $invalidate = isset($data['account_members']) ? $data['account_members'] : array(); + $invalidate[] = $data['account_id']; + self::cache_invalidate($invalidate); + + return $id; + } + + /** + * Delete one account, deletes also all acl-entries for that account + * + * @param int|string $id numeric account_id or string with account_lid + * @return boolean true on success, false otherwise + */ + function delete($id) + { + if (!is_int($id) && !is_numeric($id)) + { + $id = $this->name2id($id); + } + if (!$id) return false; + + if ($this->get_type($id) == 'u') + { + $invalidate = $this->memberships($id, true); + } + else + { + $invalidate = $this->members($id, true, false); + } + $invalidate[] = $id; + + $this->backend->delete($id); + + self::cache_invalidate($invalidate); + + // delete all acl_entries belonging to that user or group + $GLOBALS['egw']->acl->delete_account($id); + + // delete all categories belonging to that user or group + categories::delete_account($id); + + return true; + } + + /** + * test if an account is expired + * + * Can be used static if array with user-data is supplied + * + * @param array $data =null array with account data, not specifying the account is depricated!!! + * @return boolean true=expired (no more login possible), false otherwise + */ + function is_expired($data=null) + { + if (is_null($data)) $data = $this->data; // depricated use + + $expires = isset($data['account_expires']) ? $data['account_expires'] : $data['expires']; + + return $expires != -1 && $expires < time(); + } + + /** + * Test if an account is active - NOT deactivated or expired + * + * Can be used static if array with user-data is supplied + * + * @param int|array $data account_id or array with account-data + * @return boolean false if account does not exist, is expired or decativated, true otherwise + */ + function is_active($data) + { + if (!is_array($data)) $data = $this->read($data); + + return $data && !(self::is_expired($data) || $data['account_status'] != 'A'); + } + + /** + * convert an alphanumeric account-value (account_lid, account_email) to the account_id + * + * Please note: + * - if a group and an user have the same account_lid the group will be returned (LDAP only) + * - if multiple user have the same email address, the returned user is undefined + * + * @param string $name value to convert + * @param string $which ='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname + * @param string $account_type =null u = user or g = group, or default null = try both + * @return int|false numeric account_id or false on error ($name not found) + */ + function name2id($name,$which='account_lid',$account_type=null) + { + // Don't bother searching for empty or non-scalar account_lid + if(empty($name) || !is_scalar($name)) + { + return False; + } + + self::setup_cache(); + $name_list = &self::$cache['name_list']; + + if(@isset($name_list[$which][$name]) && $name_list[$which][$name]) + { + return $name_list[$which][$name]; + } + + return $name_list[$which][$name] = $this->backend->name2id($name,$which,$account_type); + } + + /** + * Convert an numeric account_id to any other value of that account (account_lid, account_email, ...) + * + * Uses the read method to fetch all data. + * + * @param int|string $account_id numeric account_id or account_lid + * @param string $which ='account_lid' type to convert to: account_lid (default), account_email, ... + * @return string|boolean converted value or false on error ($account_id not found) + */ + static function id2name($account_id, $which='account_lid') + { + if (!is_numeric($account_id) && !($account_id = self::getInstance()->name2id($account_id))) + { + return false; + } + try { + if (!($data = self::cache_read($account_id))) return false; + } + catch (Exception $e) { + unset($e); + return false; + } + return $data[$which]; + } + + /** + * get the type of an account: 'u' = user, 'g' = group + * + * @param int|string $account_id numeric account-id or alphanum. account-lid, + * if !$accountid account of the user of this session + * @return string/false 'u' = user, 'g' = group or false on error ($accountid not found) + */ + function get_type($account_id) + { + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id); + } + return $account_id > 0 ? 'u' : ($account_id < 0 ? 'g' : false); + } + + /** + * check if an account exists and if it is an user or group + * + * @param int|string $account_id numeric account_id or account_lid + * @return int 0 = acount does not exist, 1 = user, 2 = group + */ + function exists($account_id) + { + if (!($data = $this->read($account_id))) + { + return 0; + } + return $data['account_type'] == 'u' ? 1 : 2; + } + + /** + * Checks if a given account is visible to current user + * + * Not all existing accounts are visible because off account_selection preference: 'none' or 'groupmembers' + * + * @param int|string $account_id nummeric account_id or account_lid + * @return boolean true = account is visible, false = account not visible, null = account does not exist + */ + function visible($account_id) + { + if (!is_numeric($account_id)) // account_lid given + { + $account_lid = $account_id; + if (!($account_id = $this->name2id($account_lid))) return null; + } + else + { + if (!($account_lid = $this->id2name($account_id))) return null; + } + if (!isset($GLOBALS['egw_info']['user']['apps']['admin']) && + // do NOT allow other user, if account-selection is none + ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'none' && + $account_lid != $GLOBALS['egw_info']['user']['account_lid'] || + // only allow group-members for account-selection is groupmembers + $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'groupmembers' && + !array_intersect((array)$this->memberships($account_id,true), + (array)$this->memberships($GLOBALS['egw_info']['user']['account_id'],true)))) + { + //error_log(__METHOD__."($account_id='$account_lid') returning FALSE"); + return false; // user is not allowed to see given account + } + return true; // user allowed to see given account + } + + /** + * Get all memberships of an account $account_id / groups the account is a member off + * + * @param int|string $account_id numeric account-id or alphanum. account-lid + * @param boolean $just_id =false return just account_id's or account_id => account_lid pairs + * @return array with account_id's ($just_id) or account_id => account_lid pairs (!$just_id) + */ + function memberships($account_id, $just_id=false) + { + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id,'account_lid','u'); + } + if ($account_id && ($data = self::cache_read($account_id))) + { + $ret = $just_id && $data['memberships'] ? array_keys($data['memberships']) : $data['memberships']; + } + //error_log(__METHOD__."($account_id, $just_id) data=".array2string($data)." returning ".array2string($ret)); + return $ret; + } + + /** + * Sets the memberships of a given account + * + * @param array $groups array with gidnumbers + * @param int $account_id uidnumber + */ + function set_memberships($groups,$account_id) + { + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id); + } + if (($old_memberships = $this->memberships($account_id, true)) != $groups) + { + $this->backend->set_memberships($groups, $account_id); + + if (!$old_memberships) $old_memberships = array(); + self::cache_invalidate(array_unique(array_merge( + array($account_id), + array_diff($old_memberships, $groups), + array_diff($groups, $old_memberships) + ))); + } + } + + /** + * Get all members of the group $account_id + * + * @param int|string $account_id ='' numeric account-id or alphanum. account-lid, + * default account of the user of this session + * @param boolean $just_id =false return just an array of id's and not id => lid pairs, default false + * @param boolean $active =false true: return only active (not expired or deactived) members, false: return all accounts + * @return array with account_id ($just_id) or account_id => account_lid pairs (!$just_id) + */ + function members($account_id, $just_id=false, $active=true) + { + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id); + } + if ($account_id && ($data = self::cache_read($account_id, $active))) + { + $members = $active ? $data['members-active'] : $data['members']; + + return $just_id && $members ? array_keys($members) : $members; + } + return null; + } + + /** + * Set the members of a group + * + * @param array $members array with uidnumber or uid's + * @param int $gid gidnumber of group to set + */ + function set_members($members,$gid) + { + if (($old_members = $this->members($gid, true, false)) != $members) + { + $this->backend->set_members($members, $gid); + + self::cache_invalidate(array_unique(array_merge( + array($gid), + array_diff($old_members, $members), + array_diff($members, $old_members) + ))); + } + } + + /** + * splits users and groups from a array of id's or the accounts with run-rights for a given app-name + * + * @param array $app_users array of user-id's or app-name (if you use app-name the result gets cached!) + * @param string $use what should be returned only an array with id's of either 'accounts' or 'groups'. + * Or an array with arrays for 'both' under the keys 'groups' and 'accounts' or 'merge' for accounts + * and groups merged into one array + * @param boolean $active =false true: return only active (not expired or deactived) members, false: return all accounts + * @return array/boolean see $use, false on error (wront $use) + */ + function split_accounts($app_users,$use='both',$active=true) + { + if (!is_array($app_users)) + { + self::setup_cache(); + $cache = &self::$cache['account_split'][$app_user]; + + if (is_array($cache)) + { + return $cache; + } + $app_users = $GLOBALS['egw']->acl->get_ids_for_location('run',1,$app_users); + } + $accounts = array( + 'accounts' => array(), + 'groups' => array(), + ); + foreach($app_users as $id) + { + $type = $this->get_type($id); + if($type == 'g') + { + $accounts['groups'][$id] = $id; + if ($use != 'groups') + { + foreach((array)$this->members($id, true, $active) as $id) + { + $accounts['accounts'][$id] = $id; + } + } + } + else + { + $accounts['accounts'][$id] = $id; + } + } + + // not sure why they need to be sorted, but we need to remove the keys anyway + sort($accounts['groups']); + sort($accounts['accounts']); + + if (isset($cache)) + { + $cache = $accounts; + } + + switch($use) + { + case 'both': + return $accounts; + case 'groups': + return $accounts['groups']; + case 'accounts': + return $accounts['accounts']; + case 'merge': + return array_merge($accounts['accounts'],$accounts['groups']); + } + return False; + } + + /** + * Add an account for an authenticated user + * + * Expiration date and primary group are read from the system configuration. + * + * @param string $account_lid + * @param string $passwd + * @param array $GLOBALS['auto_create_acct'] values for 'firstname', 'lastname', 'email' and 'primary_group' + * @return int|boolean account_id or false on error + */ + function auto_add($account_lid, $passwd) + { + $expires = !isset($this->config['auto_create_expire']) || + $this->config['auto_create_expire'] == 'never' ? -1 : + time() + $this->config['auto_create_expire'] + 2; + + $memberships = array(); + $default_group_id = null; + // check if we have a comma or semicolon delimited list of groups --> add first as primary and rest as memberships + foreach(preg_split('/[,;] */',$this->config['default_group_lid']) as $group_lid) + { + if (($group_id = $this->name2id($group_lid,'account_lid','g'))) + { + if (!$default_group_id) $default_group_id = $group_id; + $memberships[] = $group_id; + } + } + if (!$default_group_id && ($default_group_id = $this->name2id('Default','account_lid','g'))) + { + $memberships[] = $default_group_id; + } + + $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && + $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) === 'g' ? + (int)$GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; + if ($primary_group && !in_array($primary_group, $memberships)) + { + $memberships[] = $primary_group; + } + $data = array( + 'account_lid' => $account_lid, + 'account_type' => 'u', + 'account_passwd' => $passwd, + 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', + 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', + 'account_email' => $GLOBALS['auto_create_acct']['email'], + 'account_status' => 'A', + 'account_expires' => $expires, + 'account_primary_group' => $primary_group, + ); + // use given account_id, if it's not already used + if (isset($GLOBALS['auto_create_acct']['account_id']) && + is_numeric($GLOBALS['auto_create_acct']['account_id']) && + !$this->id2name($GLOBALS['auto_create_acct']['account_id'])) + { + $data['account_id'] = $GLOBALS['auto_create_acct']['account_id']; + } + if (!($data['account_id'] = $this->save($data))) + { + return false; + } + // set memberships if given + if ($memberships) + { + $this->set_memberships($memberships,$data['account_id']); + } + // set the appropriate value for the can change password flag (assume users can, if the admin requires users to change their password) + $data['changepassword'] = (bool)$GLOBALS['egw_info']['server']['change_pwd_every_x_days']; + if(!$data['changepassword']) + { + $GLOBALS['egw']->acl->add_repository('preferences','nopasswordchange',$data['account_id'],1); + } + else + { + $GLOBALS['egw']->acl->delete_repository('preferences','nopasswordchange',$data['account_id']); + } + // call hook to notify interested apps about the new account + $GLOBALS['hook_values'] = $data; + $GLOBALS['egw']->hooks->process($data+array( + 'location' => 'addaccount', + // at login-time only the hooks from the following apps will be called + 'order' => array('felamimail','fudforum'), + ),False,True); // called for every app now, not only enabled ones + unset($data['changepassword']); + + return $data['account_id']; + } + + /** + * Update the last login timestamps and the IP + * + * @param int $account_id + * @param string $ip + * @return int lastlogin time + */ + function update_lastlogin($account_id, $ip) + { + return $this->backend->update_lastlogin($account_id, $ip); + } + + /** + * Query if backend allows to change username aka account_lid + * + * @return boolean false if backend does NOT allow it (AD), true otherwise (SQL, LDAP) + */ + function change_account_lid_allowed() + { + $change_account_lid = constant(get_class($this->backend).'::CHANGE_ACCOUNT_LID'); + if (!isset($change_account_lid)) $change_account_lid = true; + return $change_account_lid; + } + + /** + * Query if backend requires password to be set, before allowing to enable an account + * + * @return boolean true if backend requires a password (AD), false or null otherwise (SQL, LDAP) + */ + function require_password_for_enable() + { + return constant(get_class($this->backend).'::REQUIRE_PASSWORD_FOR_ENABLE'); + } + + /** + * Invalidate cache (or parts of it) after change in $account_ids + * + * We use now an instance-wide read-cache storing account-data and members(hips). + * + * @param int|array $account_ids user- or group-id(s) for which cache should be invalidated, default 0 = only search/name2id cache + */ + static function cache_invalidate($account_ids=0) + { + //error_log(__METHOD__.'('.array2string($account_ids).')'); + + // instance-wide cache + if ($account_ids) + { + foreach((array)$account_ids as $account_id) + { + $instance = self::getInstance(); + + Cache::unsetCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id); + + unset(self::$request_cache[$account_id]); + } + } + + // session-cache + if (self::$cache) self::$cache = array(); + Cache::unsetSession('accounts_cache','phpgwapi'); + + if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited + { + egw::invalidate_session_cache(); // invalidates whole egw-enviroment if stored in the session + } + } + + /** + * Timeout of instance wide cache for reading account-data and members(hips) + */ + const READ_CACHE_TIMEOUT = 43200; + + /** + * Local per request cache, to minimize calls to instance cache + * + * @var array + */ + static $request_cache = array(); + + /** + * Read account incl. members/memberships from cache (or backend and cache it) + * + * @param int $account_id + * @param boolean $need_active =false true = 'members-active' required + * @return array + * @throws egw_exception_wrong_parameter if no integer was passed as $account_id + */ + static function cache_read($account_id, $need_active=false) + { + if (!is_numeric($account_id)) throw new egw_exception_wrong_parameter('Not an integer!'); + + $account =& self::$request_cache[$account_id]; + + if (!isset($account)) // not in request cache --> try instance cache + { + $instance = self::getInstance(); + + $account = Cache::getCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id); + + if (!isset($account)) // not in instance cache --> read from backend + { + if (($account = $instance->backend->read($account_id))) + { + if ($instance->get_type($account_id) == 'u') + { + if (!isset($account['memberships'])) $account['memberships'] = $instance->backend->memberships($account_id); + } + else + { + if (!isset($account['members'])) $account['members'] = $instance->backend->members($account_id); + } + Cache::setCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id, $account, self::READ_CACHE_TIMEOUT); + } + //error_log(__METHOD__."($account_id) read from backend ".array2string($account)); + } + //else error_log(__METHOD__."($account_id) read from instance cache ".array2string($account)); + } + // if required and not already set, query active members AND cache them too + if ($need_active && $account_id < 0 && !isset($account['members-active'])) + { + $instance = self::getInstance(); + $account['members-active'] = array(); + foreach((array)$account['members'] as $id => $lid) + { + if ($instance->is_active($id)) $account['members-active'][$id] = $lid; + } + Cache::setCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id, $account, self::READ_CACHE_TIMEOUT); + } + //error_log(__METHOD__."($account_id, $need_active) returning ".array2string($account)); + return $account; + } + + /** + * Internal functions not meant to use outside this class!!! + */ + + /** + * Sets up session cache, now only used for search and name2id list + * + * Other account-data is cached on instance-level + * + * The cache is shared between all instances of the account-class and it can be save in the session, + * if use_session_cache is set to True + * + * @internal + */ + private static function setup_cache() + { + if (is_array(self::$cache)) return; // cache is already setup + + if (self::$use_session_cache && is_object($GLOBALS['egw']->session)) + { + self::$cache =& Cache::getSession('accounts_cache','phpgwapi'); + } + //error_log(__METHOD__."() use_session_cache=".array2string(self::$use_session_cache).", is_array(self::\$cache)=".array2string(is_array(self::$cache))); + + if (!is_array(self::$cache)) + { + self::$cache = array(); + } + } +} diff --git a/phpgwapi/inc/class.accounts_ads.inc.php b/api/src/Accounts/Ads.php similarity index 97% rename from phpgwapi/inc/class.accounts_ads.inc.php rename to api/src/Accounts/Ads.php index 0952797997..6113413b6f 100644 --- a/phpgwapi/inc/class.accounts_ads.inc.php +++ b/api/src/Accounts/Ads.php @@ -11,7 +11,12 @@ * @version $Id$ */ +namespace EGroupware\Api\Accounts; + +use EGroupware\Api; + require_once EGW_API_INC.'/adldap/adLDAP.php'; +use adLDAPException; /** * Active directory backend for accounts @@ -31,7 +36,7 @@ require_once EGW_API_INC.'/adldap/adLDAP.php'; * @link http://www.selfadsi.org/attributes-e2k7.htm * @link http://msdn.microsoft.com/en-us/library/ms675090(v=vs.85).aspx */ -class accounts_ads +class Ads { /** * Instance of adLDAP class @@ -113,7 +118,6 @@ class accounts_ads * * @param accounts $frontend reference to the frontend class, to be able to call it's methods if needed * @throws adLDAPException - * @return accounts_ldap */ function __construct(accounts $frontend) { @@ -136,8 +140,8 @@ class accounts_ads if (!isset($adldap[$config['ads_domain']])) { - if (empty($config['ads_host'])) throw new Exception("Required ADS host name(s) missing!"); - if (empty($config['ads_domain'])) throw new Exception("Required ADS domain missing!"); + if (empty($config['ads_host'])) throw new Api\Exception("Required ADS host name(s) missing!"); + if (empty($config['ads_domain'])) throw new Api\Exception("Required ADS domain missing!"); $base_dn_parts = array(); foreach(explode('.', $config['ads_domain']) as $dc) @@ -153,9 +157,9 @@ class accounts_ads 'admin_password' => $config['ads_admin_passwd'], 'use_tls' => $config['ads_connection'] == 'tls', 'use_ssl' => $config['ads_connection'] == 'ssl', - 'charset' => translation::charset(), + 'charset' => Api\Translation::charset(), ); - $adldap[$config['ads_domain']] = new adLDAP_egw($options); + $adldap[$config['ads_domain']] = new adLDAP($options); if (self::$debug) error_log(__METHOD__."() new adLDAP(".array2string($options).") returned ".array2string($adldap[$config['ads_domain']]).' '.function_backtrace()); } //else error_log(__METHOD__."() returning cached adLDAP ".array2string($adldap[$config['ads_domain']]).' '.function_backtrace()); @@ -173,7 +177,7 @@ class accounts_ads static $domain_sid = null; if (!isset($domain_sid)) { - $domain_sid = egw_cache::getCache($this->frontend->config['install_id'], __CLASS__, 'ads_domain_sid'); + $domain_sid = Api\Cache::getCache($this->frontend->config['install_id'], __CLASS__, 'ads_domain_sid'); if ((!is_array($domain_sid) || !isset($domain_sid[$this->frontend->config['ads_domain']])) && ($adldap = self::get_adldap($this->frontend->config)) && ($sr = ldap_search($adldap->getLdapConnection(), $adldap->getBaseDn(), '(objectclass=domain)', array('objectsid'))) && @@ -181,7 +185,7 @@ class accounts_ads { $domain_sid = array(); $domain_sid[$this->frontend->config['ads_domain']] = $adldap->utilities()->getTextSID($entries[0]['objectsid'][0]); - egw_cache::setCache($this->frontend->config['install_id'], __CLASS__, 'ads_domain_sid', $domain_sid); + Api\Cache::setCache($this->frontend->config['install_id'], __CLASS__, 'ads_domain_sid', $domain_sid); } } $sid = $domain_sid[$this->frontend->config['ads_domain']]; @@ -234,7 +238,7 @@ class accounts_ads $matches = null; if (!preg_match('/^(.*),'.preg_quote($base, '/').'$/i', $context, $matches)) { - throw new egw_exception_wrong_userinput("Wrong or not configured ADS context '$context' (baseDN='$base')!"); + throw new Api\Exception\WrongUserinput("Wrong or not configured ADS context '$context' (baseDN='$base')!"); } $container = $matches[1]; if (self::$debug) error_log(__METHOD__."() context='$context', base='$base' returning ".array2string($container)); @@ -348,7 +352,7 @@ class accounts_ads function save(&$data) { $is_group = $data['account_id'] < 0 || $data['account_type'] === 'g'; - $data = translation::convert($data, translation::charset(), 'utf-8'); + $data = Api\Translation::convert($data, Api\Translation::charset(), 'utf-8'); if ($data['account_id'] && !($old = $this->read($data['account_id']))) { @@ -362,7 +366,7 @@ class accounts_ads error_log(__METHOD__.'('.array2string($data).") changing account-type user <--> group forbidden!"); return false; } - $old = translation::convert($old, translation::charset(), 'utf-8'); + $old = Api\Translation::convert($old, Api\Translation::charset(), 'utf-8'); } $ret = $is_group ? $this->_save_group($data, $old) : $this->_save_user($data, $old); @@ -408,7 +412,7 @@ class accounts_ads */ protected function _ldap2group($_data) { - $data = translation::convert($_data, 'utf-8'); + $data = Api\Translation::convert($_data, 'utf-8'); // no need to calculate sid, if already calculated $sid = is_string($data['objectsid']) ? $data['objectsid'] : @@ -469,7 +473,7 @@ class accounts_ads */ protected function _ldap2user(array $_data) { - $data = translation::convert($_data, 'utf-8'); + $data = Api\Translation::convert($_data, 'utf-8'); // no need to calculate sid, if already calculated $sid = is_string($data['objectsid']) ? $data['objectsid'] : @@ -561,8 +565,8 @@ class accounts_ads if (!isset($utc)) $utc = new DateTimeZone('UTC'); list($when) = explode('.', $_when); // remove .0Z not understood by createFromFormat - $datetime = egw_time::createFromFormat(self::WHEN_FORMAT, $when, $utc); - if (egw_time::$server_timezone) $datetime->setTimezone(egw_time::$server_timezone); + $datetime = Api\DateTime::createFromFormat(self::WHEN_FORMAT, $when, $utc); + if (Api\DateTime::$server_timezone) $datetime->setTimezone(Api\DateTime::$server_timezone); return $datetime->getTimestamp(); } @@ -945,7 +949,6 @@ class accounts_ads $account_search[$unl_serial]['total'] = $this->total = count($accounts); } - //echo "

accounts_ldap::search() found $this->total: ".microtime()."

\n"; // return only the wanted accounts reset($sortedAccounts); if(is_numeric($start) && is_numeric($offset)) @@ -1072,7 +1075,7 @@ class accounts_ads continue; // ignore system accounts incl. "Administrator" } $accounts[($data['samaccounttype'][0] == adLDAP::ADLDAP_SECURITY_GLOBAL_GROUP ? '-' : '').$rid] = - $attrs ? $data : translation::convert($data['samaccountname'][0], 'utf-8'); + $attrs ? $data : Api\Translation::convert($data['samaccountname'][0], 'utf-8'); } } else if (self::$debug) error_log(__METHOD__.'('.array2string($attr_filter).", '$account_type') ldap_search($ds, '$context', '$filter')=$sri allValues=".array2string($allValues)); @@ -1241,7 +1244,7 @@ class accounts_ads * - allow to use utf-8 charset internally, not just an 8-bit iso-charset * - support for Windows2008r2 (maybe earlier too) and Samba4 "CN=Users" DN as container to create users or groups */ -class adLDAP_egw extends adLDAP +class adLDAP extends \adLDAP { /** * Charset used for internal encoding @@ -1286,7 +1289,7 @@ class adLDAP_egw extends adLDAP */ public function user() { if (!$this->userClass) { - $this->userClass = new adLDAPUsers_egw($this); + $this->userClass = new adLDAPUsers($this); } return $this->userClass; } @@ -1298,7 +1301,7 @@ class adLDAP_egw extends adLDAP */ public function group() { if (!$this->groupClass) { - $this->groupClass = new adLDAPGroups_egw($this); + $this->groupClass = new adLDAPGroups($this); } return $this->groupClass; } @@ -1310,7 +1313,7 @@ class adLDAP_egw extends adLDAP */ public function utilities() { if (!$this->utilClass) { - $this->utilClass = new adLDAPUtils_egw($this); + $this->utilClass = new adLDAPUtils($this); } return $this->utilClass; } @@ -1319,7 +1322,7 @@ class adLDAP_egw extends adLDAP /** * Fixes an enhancements for adLDAPUser required by EGroupware */ -class adLDAPUsers_egw extends adLDAPUsers +class adLDAPUsers extends \adLDAPUsers { /** * Create a user @@ -1579,7 +1582,7 @@ class adLDAPUsers_egw extends adLDAPUsers /** * Fixes an enhancements for adLDAPGroups required by EGroupware */ -class adLDAPGroups_egw extends adLDAPGroups +class adLDAPGroups extends \adLDAPGroups { /** * Create a group @@ -1623,7 +1626,7 @@ class adLDAPGroups_egw extends adLDAPGroups /** * Fixes an enhancements for adLDAPUtils required by EGroupware */ -class adLDAPUtils_egw extends adLDAPUtils +class adLDAPUtils extends \adLDAPUtils { /** * Convert 8bit characters e.g. accented characters to UTF8 encoded characters diff --git a/phpgwapi/inc/class.accounts_ldap.inc.php b/api/src/Accounts/Ldap.php similarity index 93% rename from phpgwapi/inc/class.accounts_ldap.inc.php rename to api/src/Accounts/Ldap.php index 67872af356..363820cc31 100644 --- a/phpgwapi/inc/class.accounts_ldap.inc.php +++ b/api/src/Accounts/Ldap.php @@ -17,6 +17,14 @@ * @version $Id$ */ +namespace EGroupware\Api\Accounts; + +use EGroupware\Api; + +// explicitly reference classes still in phpgwapi or old structure +use common; // next_id +use auth; + /** * LDAP Backend for accounts * @@ -34,11 +42,9 @@ * * @author Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package api - * @subpackage accounts * @access internal only use the interface provided by the accounts class */ -class accounts_ldap +class Ldap { /** * Name of mail attribute @@ -144,7 +150,6 @@ class accounts_ldap * Constructor * * @param accounts $frontend reference to the frontend class, to be able to call it's methods if needed - * @return accounts_ldap */ function __construct(accounts $frontend) { @@ -153,7 +158,7 @@ class accounts_ldap // enable the caching in the session, done by the accounts class extending this class. $this->use_session_cache = true; - $this->ldap = new ldap(true); + $this->ldap = new Api\Ldap(true); $this->ds = $this->ldap->ldapConnect($this->frontend->config['ldap_host'], $this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw']); @@ -192,7 +197,7 @@ class accounts_ldap { $is_group = $data['account_id'] < 0 || $data['account_type'] === 'g'; - $data_utf8 = translation::convert($data,translation::charset(),'utf-8'); + $data_utf8 = Api\Translation::convert($data,Api\Translation::charset(),'utf-8'); $members = $data['account_members']; if (!is_object($this->ldapServerInfo)) @@ -214,7 +219,7 @@ class accounts_ldap } else { - $old = ldap::result2array($old[0]); + $old = Api\Ldap::result2array($old[0]); $old['objectclass'] = array_map('strtolower', $old['objectclass']); $key = false; if ($is_group && ($key = array_search('namedobject',$old['objectclass'])) !== false || @@ -359,7 +364,7 @@ class accounts_ldap if (!$GLOBALS['egw_info']['server']['ldap_allow_systemusernames'] && !$old && function_exists('posix_getpwnam') && posix_getpwnam($data['account_lid'])) { - throw new egw_exception_wrong_userinput(lang('There already is a system-user with this name. User\'s should not have the same name as a systemuser')); + throw new Api\Exception\WrongUserinput(lang('There already is a system-user with this name. User\'s should not have the same name as a systemuser')); } } @@ -367,7 +372,6 @@ class accounts_ldap if(!$old && is_array($to_write['memberuid']) && empty($to_write['memberuid'])) { unset($to_write['memberuid']); } - //echo "

ldap_".($old ? 'modify' : 'add')."(,$dn,".print_r($to_write,true).")

\n"; // modifying or adding the entry if ($old && !@ldap_modify($this->ds,$dn,$to_write) || !$old && !@ldap_add($this->ds,$dn,$to_write)) @@ -453,7 +457,7 @@ class accounts_ldap { return false; // group not found } - $data = translation::convert($ldap_data[0],'utf-8'); + $data = Api\Translation::convert($ldap_data[0],'utf-8'); unset($data['objectclass']['count']); $group += array( @@ -504,7 +508,7 @@ class accounts_ldap { return false; // user not found } - $data = translation::convert($ldap_data[0],'utf-8'); + $data = Api\Translation::convert($ldap_data[0],'utf-8'); $utc_diff = date('Z'); $user = array( @@ -534,7 +538,7 @@ class accounts_ldap 'account_created' => isset($data['createtimestamp'][0]) ? self::accounts_ldap2ts($data['createtimestamp'][0]) : null, 'account_modified' => isset($data['modifytimestamp'][0]) ? self::accounts_ldap2ts($data['modifytimestamp'][0]) : null, ); - //echo "

accounts_ldap::_read_user($account_id): shadowexpire={$data['shadowexpire'][0]} --> account_expires=$user[account_expires]=".date('Y-m-d H:i',$user['account_expires'])."

\n"; + if ($this->frontend->config['ldap_extra_attributes']) { $user['homedirectory'] = $data['homedirectory'][0]; @@ -573,8 +577,6 @@ class accounts_ldap */ protected function _merge_user($to_write,$data,$new_entry) { - //echo "

accounts_ldap::_merge_user(".print_r($to_write,true).','.print_r($data,true).",$new_entry)

\n"; - $to_write['uidnumber'] = $data['account_id']; $to_write['uid'] = $data['account_lid']; $to_write['gidnumber'] = abs($data['account_primary_group']); @@ -594,7 +596,7 @@ class accounts_ldap { if (preg_match('/^[a-f0-9]{32}$/', $data['account_passwd'])) // md5 --> ldap md5 { - $data['account_passwd'] = setup_cmd_ldap::hash_sql2ldap($data['account_passwd']); + $data['account_passwd'] = setup_cmd_Api\Ldap::hash_sql2ldap($data['account_passwd']); } elseif (!preg_match('/^\\{[a-z5]{3,5}\\}.+/i',$data['account_passwd'])) // if it's not already entcrypted, do so now { @@ -609,7 +611,7 @@ class accounts_ldap // - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60) $shadowexpire = ($data['account_expires']-$utc_diff) / (24*3600); - //echo "

account_expires=".date('Y-m-d H:i',$data['account_expires'])." --> $shadowexpire --> ".date('Y-m-d H:i',$account_expire)."

\n"; + $to_write['shadowexpire'] = !$data['account_status'] ? ($data['account_expires'] != -1 && $data['account_expires'] < time() ? round($shadowexpire) : 0) : ($data['account_expires'] != -1 ? round($shadowexpire) : array()); // array() = unset value @@ -660,7 +662,7 @@ class accounts_ldap function search($param) { //error_log(__METHOD__."(".array2string($param).")"); - $account_search =& accounts::$cache['account_search']; + $account_search =& Api\Accounts::$cache['account_search']; // check if the query is cached $serial = serialize($param); @@ -683,7 +685,7 @@ class accounts_ldap } else // we need to run the unlimited query { - $query = ldap::quote(strtolower($param['query'])); + $query = Api\Ldap::quote(strtolower($param['query'])); $accounts = array(); if($param['type'] != 'groups') @@ -762,7 +764,6 @@ class accounts_ldap $filter = str_replace(array('%user','%domain'),array('*',$GLOBALS['egw_info']['user']['domain']),$filter); } $sri = ldap_search($this->ds, $this->user_context, $filter,array('uid','uidNumber','givenname','sn',static::MAIL_ATTR,'shadowExpire','createtimestamp','modifytimestamp','objectclass','gidNumber')); - //echo "

ldap_search(,$this->user_context,'$filter',) ".($sri ? '' : ldap_error($this->ds)).microtime()."

\n"; $utc_diff = date('Z'); foreach(ldap_get_entries($this->ds, $sri) as $allVals) @@ -773,10 +774,10 @@ class accounts_ldap { $account = Array( 'account_id' => $allVals['uidnumber'][0], - 'account_lid' => translation::convert($allVals['uid'][0],'utf-8'), + 'account_lid' => Api\Translation::convert($allVals['uid'][0],'utf-8'), 'account_type' => 'u', - 'account_firstname' => translation::convert($allVals['givenname'][0],'utf-8'), - 'account_lastname' => translation::convert($allVals['sn'][0],'utf-8'), + 'account_firstname' => Api\Translation::convert($allVals['givenname'][0],'utf-8'), + 'account_lastname' => Api\Translation::convert($allVals['sn'][0],'utf-8'), 'account_status' => isset($allVals['shadowexpire'][0]) && $allVals['shadowexpire'][0]*24*3600-$utc_diff < time() ? false : 'A', 'account_expires' => isset($allVals['shadowexpire']) && $allVals['shadowexpire'][0] ? $allVals['shadowexpire'][0]*24*3600+$utc_diff : -1, // LDAP date is in UTC 'account_email' => $allVals[static::MAIL_ATTR][0], @@ -790,7 +791,8 @@ class accounts_ldap if (isset($totalcount)) --$totalcount; continue; } - $account['account_fullname'] = common::display_fullname($account['account_lid'],$account['account_firstname'],$account['account_lastname'],$allVals['uidnumber'][0]); + $account['account_fullname'] = Api\Accounts::format_username($account['account_lid'], + $account['account_firstname'], $account['account_lastname'], $allVals['uidnumber'][0]); // return objectclass(es) if ($param['objectclass']) { @@ -832,12 +834,12 @@ class accounts_ldap { $accounts[(string)-$allVals['gidnumber'][0]] = Array( 'account_id' => -$allVals['gidnumber'][0], - 'account_lid' => translation::convert($allVals['cn'][0],'utf-8'), + 'account_lid' => Api\Translation::convert($allVals['cn'][0],'utf-8'), 'account_type' => 'g', - 'account_firstname' => translation::convert($allVals['cn'][0],'utf-8'), + 'account_firstname' => Api\Translation::convert($allVals['cn'][0],'utf-8'), 'account_lastname' => lang('Group'), 'account_status' => 'A', - 'account_fullname' => translation::convert($allVals['cn'][0],'utf-8'), + 'account_fullname' => Api\Translation::convert($allVals['cn'][0],'utf-8'), ); if (isset($totalcount)) ++$totalcount; } @@ -857,7 +859,6 @@ class accounts_ldap $account_search[$unl_serial]['total'] = $this->total; } } - //echo "

accounts_ldap::search() found $this->total: ".microtime()."

\n"; // return only the wanted accounts reset($sortedAccounts); if(is_numeric($start) && is_numeric($offset)) @@ -939,7 +940,7 @@ class accounts_ldap */ function name2id($_name,$which='account_lid',$account_type=null) { - $name = ldap::quote(translation::convert($_name,translation::charset(),'utf-8')); + $name = Api\Ldap::quote(Api\Translation::convert($_name,Api\Translation::charset(),'utf-8')); if ($which == 'account_lid' && $account_type !== 'u') // groups only support account_lid { @@ -1009,7 +1010,7 @@ class accounts_ldap { if (!(int) $account_id || !($account_lid = $this->id2name($account_id))) return false; - $sri = ldap_search($this->ds,$this->group_context,'(&(objectClass=posixGroup)(memberuid='.ldap::quote($account_lid).'))',array('cn','gidnumber')); + $sri = ldap_search($this->ds,$this->group_context,'(&(objectClass=posixGroup)(memberuid='.Api\Ldap::quote($account_lid).'))',array('cn','gidnumber')); $memberships = array(); foreach((array)ldap_get_entries($this->ds, $sri) as $key => $data) { @@ -1017,7 +1018,6 @@ class accounts_ldap $memberships[(string) -$data['gidnumber'][0]] = $data['cn'][0]; } - //echo "accounts::memberships($account_id)"; _debug_array($memberships); return $memberships; } @@ -1054,7 +1054,6 @@ class accounts_ldap } } } - //echo "accounts_ldap::members($gid)"; _debug_array($members); return $members; } @@ -1066,8 +1065,6 @@ class accounts_ldap */ function set_memberships($groups,$account_id) { - //echo "

accounts_ldap::set_memberships(".print_r($groups,true).",$account_id)

\n"; - // remove not longer existing memberships if (($old_memberships = $this->memberships($account_id))) { @@ -1102,7 +1099,6 @@ class accounts_ldap */ function set_members($members, $gid, array $objectclass=null, $use_cn=null) { - //echo "

accounts_ldap::set_members(".print_r($members,true).",$gid)

\n"; if (!($cn = $use_cn) && !($cn = $this->id2name($gid))) return false; // do that group is a groupOf(Unique)Names or univentionGroup? @@ -1154,7 +1150,7 @@ class accounts_ldap } } } - if (!ldap_modify($this->ds,'cn='.ldap::quote($cn).','.$this->group_context,$to_write)) + if (!ldap_modify($this->ds,'cn='.Api\Ldap::quote($cn).','.$this->group_context,$to_write)) { echo "ldap_modify(,'cn=$cn,$this->group_context',".print_r($to_write,true)."))\n"; return false; diff --git a/phpgwapi/inc/class.accounts_sql.inc.php b/api/src/Accounts/Sql.php similarity index 93% rename from phpgwapi/inc/class.accounts_sql.inc.php rename to api/src/Accounts/Sql.php index bd968da76d..854764dbc4 100644 --- a/phpgwapi/inc/class.accounts_sql.inc.php +++ b/api/src/Accounts/Sql.php @@ -23,21 +23,27 @@ * @version $Id$ */ +namespace EGroupware\Api\Accounts; + +use EGroupware\Api; + +// explicitly reference classes still in phpgwapi of old structure +use emailadmin_smtp_sql; +use acl; + /** * SQL Backend for accounts * * @author Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package api - * @subpackage accounts * @access internal only use the interface provided by the accounts class */ -class accounts_sql +class Sql { /** * instance of the db class * - * @var egw_db + * @var Api\Db */ var $db; /** @@ -74,9 +80,9 @@ class accounts_sql private $frontend; /** - * Instance of contacts object / addressbook_bo, NOT automatic instanciated! + * Instance of contacts object, NOT automatic instanciated! * - * @var addressbook_bo + * @var Api\Contacts */ private $contacts; @@ -93,10 +99,9 @@ class accounts_sql /** * Constructor * - * @param accounts $frontend reference to the frontend class, to be able to call it's methods if needed - * @return accounts_sql + * @param Api\Accounts $frontend reference to the frontend class, to be able to call it's methods if needed */ - function __construct(accounts $frontend) + function __construct(Api\Accounts $frontend) { $this->frontend = $frontend; @@ -171,14 +176,13 @@ class accounts_sql $data['account_lastname'] = $data['account_type'] == 'g' ? 'Group' : 'User'; // if we call lang() before the translation-class is correctly setup, // we can't switch away from english language anymore! - if (translation::$lang_arr) + if (Api\Translation::$lang_arr) { $data['account_lastname'] = lang($data['account_lastname']); } } if (!$data['account_fullname']) $data['account_fullname'] = $data['account_firstname'].' '.$data['account_lastname']; - //echo "accounts_sql::read($account_id)"; _debug_array($data); return $data; } @@ -192,7 +196,6 @@ class accounts_sql */ function save(&$data) { - //echo "

accounts_sql::save(".print_r($data,true).")

\n"; $to_write = $data; unset($to_write['account_passwd']); // encrypt password if given or unset it if not @@ -287,7 +290,7 @@ class accounts_sql } if ($contact_id) { - if (!isset($this->contacts)) $this->contacts = new addressbook_bo(); + if (!isset($this->contacts)) $this->contacts = new Api\Contacts(); $this->contacts->delete($contact_id,false); // false = allow to delete accounts (!) } return true; @@ -311,7 +314,6 @@ class accounts_sql $memberships[(string) $gid] = $this->id2name($gid); } } - //echo "accounts::memberships($account_id)"; _debug_array($memberships); return $memberships; } @@ -357,7 +359,6 @@ class accounts_sql { $members[$row['account_id']] = $row['account_lid']; } - //echo "accounts::members($accountid)"; _debug_array($members); return $members; } @@ -369,7 +370,6 @@ class accounts_sql */ function set_members($members,$gid) { - //echo "

accounts::set_members(".print_r($members,true).",$gid)

\n"; $GLOBALS['egw']->acl->delete_repository('phpgw_group',$gid,false); if (is_array($members)) @@ -410,11 +410,11 @@ class accounts_sql 'account_email' => 'contact_email', ); - // fetch order of account_fullname from common::display_fullname + // fetch order of account_fullname from Api\Accounts::format_username if (strpos($param['order'],'account_fullname') !== false) { $param['order'] = str_replace('account_fullname', preg_replace('/[ ,]+/',',',str_replace(array('[',']'),'', - common::display_fullname('account_lid','account_firstname','account_lastname'))), $param['order']); + Api\Accounts::format_username('account_lid','account_firstname','account_lastname'))), $param['order']); } $order = str_replace(array_keys($order2contact),array_values($order2contact),$param['order']); // allways add 'account_lid', as it is only valid one for groups @@ -490,7 +490,7 @@ class accounts_sql } if ($param['active']) { - $filter[] = str_replace('UNIX_TIMESTAMP(NOW())',time(),addressbook_sql::ACOUNT_ACTIVE_FILTER); + $filter[] = str_replace('UNIX_TIMESTAMP(NOW())',time(),Api\Contacts\Sql::ACOUNT_ACTIVE_FILTER); } $criteria = array(); $wildcard = $param['query_type'] == 'start' || $param['query_type'] == 'exact' ? '' : '%'; @@ -532,7 +532,7 @@ class accounts_sql break; } } - if (!isset($this->contacts)) $this->contacts = new addressbook_bo(); + if (!isset($this->contacts)) $this->contacts = new Api\Contacts(); $accounts = array(); foreach((array) $this->contacts->search($criteria, @@ -556,9 +556,9 @@ class accounts_sql 'account_status' => $contact['account_status'], 'account_expires' => $contact['account_expires'], 'account_primary_group' => $contact['account_primary_group'], - // addressbook_bo::search() returns everything in user-time, need to convert to server-time - 'account_created' => egw_time::user2server($contact['created']), - 'account_modified' => egw_time::user2server($contact['modified']), + // Api\Contacts::search() returns everything in user-time, need to convert to server-time + 'account_created' => Api\DateTime::user2server($contact['created']), + 'account_modified' => Api\DateTime::user2server($contact['modified']), 'account_description' => $contact['account_description'], ); } @@ -607,7 +607,7 @@ class accounts_sql // check if we need to treat username case-insensitive if ($which == 'account_lid' && !$GLOBALS['egw_info']['server']['case_sensitive_username']) // = is case sensitiv eg. on postgres, but not on mysql! { - $where[] = 'account_lid '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote($where['account_lid']); + $where[] = 'account_lid '.$this->db->capabilities[Api\Db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote($where['account_lid']); unset($where['account_lid']); } } diff --git a/phpgwapi/inc/class.accounts_univention.inc.php b/api/src/Accounts/Univention.php similarity index 94% rename from phpgwapi/inc/class.accounts_univention.inc.php rename to api/src/Accounts/Univention.php index a45549d3ee..e22a38fadc 100644 --- a/phpgwapi/inc/class.accounts_univention.inc.php +++ b/api/src/Accounts/Univention.php @@ -11,6 +11,11 @@ * @version $Id$ */ +namespace EGroupware\Api\Accounts; + +// explicitly reference classes still in phpgwapi +use emailadmin_account; + /** * Univention LDAP Backend for accounts * @@ -22,7 +27,7 @@ * Existing users and groups need to be renamed via same CLI, as removing and * adding entry under new dn via LDAP fails (Type or value exists). */ -class accounts_univention extends accounts_ldap +class Univention extends Ldap { /** * Attribute with mail address @@ -97,7 +102,7 @@ class accounts_univention extends accounts_ldap { $params[5] = '********'; // mask out password! $cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params)); - throw new egw_exception_wrong_userinput($cmd."\nreturned\n".$output); + throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output); } $data['account_dn'] = $matches[1]; $data['account_id'] = $this->name2id($data['account_lid'], 'account_lid', 'u'); @@ -121,7 +126,7 @@ class accounts_univention extends accounts_ldap { $params[5] = '********'; // mask out password! $cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params)); - throw new egw_exception_wrong_userinput($cmd."\nreturned\n".$output); + throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output); } $data['account_dn'] = $data['account_type'] !== 'g' ? $matches[1] : // duno why but directory-manager returns old dn for groups ... diff --git a/api/src/Contacts.php b/api/src/Contacts.php index 87bda53c27..72fe5e839a 100755 --- a/api/src/Contacts.php +++ b/api/src/Contacts.php @@ -18,7 +18,6 @@ namespace EGroupware\Api; // explicitly reference classes still in phpgwapi use categories; -use common; // grab_owner_name use egw_link; use calendar_bo; // to_do: do NOT require it, just use if there @@ -346,8 +345,6 @@ class Contacts extends Contacts\Storage */ function get_addressbooks($required=EGW_ACL_READ,$extra_label=null,$user=null) { - //echo "uicontacts::get_addressbooks($required,$include_all) grants="; _debug_array($this->grants); - if (is_null($user)) { $user = $this->user; @@ -391,7 +388,7 @@ class Contacts extends Contacts\Storage { if ($uid != $user && ($rights & $required) == $required && $GLOBALS['egw']->accounts->get_type($uid) == 'u') { - $to_sort[$uid] = common::grab_owner_name($uid); + $to_sort[$uid] = Accounts::username($uid); } } if ($to_sort) @@ -403,7 +400,6 @@ class Contacts extends Contacts\Storage { $addressbooks[$user.'p'] = lang('Private'); } - //echo "

".__METHOD__."($required,'$extra_label')"; _debug_array($addressbooks); return $addressbooks; } @@ -452,7 +448,6 @@ class Contacts extends Contacts\Storage { $fileas = substr($fileas,0,-2); } - //echo "

bocontacts::fileas(,$type)='$fileas'

\n"; return $fileas; } @@ -566,7 +561,6 @@ class Contacts extends Contacts\Storage } if ($old_fileas != $contact['n_fileas'] || $old_fn != $contact['n_fn']) { - //echo "

('$old_fileas' != '{$contact['n_fileas']}' || '$old_fn' != '{$contact['n_fn']}')=".array2string($old_fileas != $contact['n_fileas'] || $old_fn != $contact['n_fn'])."

\n"; // only specify/write updated fields plus "keys" $contact = array_intersect_key($contact,array( 'id' => true, @@ -1277,7 +1271,6 @@ class Contacts extends Contacts\Storage arsort($values,SORT_NUMERIC); list($value,$num) = each($values); - //echo "

$name: '$value' $num/".count($contacts)."=".($num / (double) count($contacts))." >= $this->org_common_factor = ".($num / (double) count($contacts) >= $this->org_common_factor ? 'true' : 'false')."

\n"; if ($value && $num / (double) count($contacts) >= $this->org_common_factor) { if (!in_array($name,$csvs)) @@ -1404,7 +1397,6 @@ class Contacts extends Contacts\Storage if ((string)$value == (string)$member[$name]) { $member[$name] = $to[$name]; - //echo "

$member[n_family], $member[n_given]: $name='{$to[$name]}'

\n"; ++$fields; } } @@ -1591,7 +1583,6 @@ class Contacts extends Contacts\Storage 'cn' => trim($contact['n_given'].' '.$contact['n_family']), ); } - //echo "

calendar_info(".print_r($ids,true).")="; _debug_array($data); return $data; } @@ -2028,7 +2019,6 @@ class Contacts extends Contacts\Storage default: $adr_format = $this->prefs['addr_format'] ? $this->prefs['addr_format'] : 'postcode_city'; } - //echo "

bocontacts::addr_format_by_country('$country'='$code') = '$adr_format'

\n"; return $adr_format; } diff --git a/api/src/Contacts/Ads.php b/api/src/Contacts/Ads.php index f97b7de9f2..a933e52ead 100644 --- a/api/src/Contacts/Ads.php +++ b/api/src/Contacts/Ads.php @@ -14,9 +14,6 @@ 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) * @@ -67,7 +64,7 @@ class Ads extends Ldap /** * Accounts ADS object * - * @var accounts_ads + * @var Api\Accounts\Acs */ protected $accounts_ads; @@ -205,7 +202,7 @@ class Ads extends Ldap $contact['id'] = $contact['uid'] = $this->accounts_ads->objectguid2str($data['objectguid']); // ignore system accounts - if ($contact['account_id'] < accounts_ads::MIN_ACCOUNT_ID) return false; + if ($contact['account_id'] < Api\Accounts\Ads::MIN_ACCOUNT_ID) return false; // ignore deactivated or expired accounts if (!$this->accounts_ads->user_active($data)) return false; diff --git a/api/src/Contacts/Storage.php b/api/src/Contacts/Storage.php index 8672a79902..8886b46a2d 100755 --- a/api/src/Contacts/Storage.php +++ b/api/src/Contacts/Storage.php @@ -16,9 +16,6 @@ namespace EGroupware\Api\Contacts; use EGroupware\Api; -// explicitly reference classes still in phpgwapi - - /** * Contacts storage object * @@ -438,7 +435,8 @@ class Storage { if ((isset($row[$this->distri_id])&&strlen($row[$this->distri_value])>0)) { - $fields[$row[$this->distri_id]][$row[$this->distri_key]] = $row[$this->distri_value].' ('.$GLOBALS['egw']->common->grab_owner_name($row[$this->distri_owner]).')'; + $fields[$row[$this->distri_id]][$row[$this->distri_key]] = $row[$this->distri_value].' ('. + Api\Accounts::username($row[$this->distri_owner]).')'; } } return $fields; @@ -606,7 +604,6 @@ class Storage */ function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='') { - //echo '

'.__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',$start,".array2string($filter,true).",'$join')

\n"; //error_log(__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',".array2string($start).','.array2string($filter,true).",'$join')"); // Handle 'None' country option @@ -772,13 +769,12 @@ class Storage $rows = $this->somain->organisations($param); $this->total = $this->somain->total; - //echo "

socontacts::organisations(".print_r($param,true).")
".$this->somain->db->Query_ID->sql."

\n"; if (!$rows) return array(); foreach($rows as $n => $row) { - if (strpos($row['org_name'],'&')!==false) $row['org_name'] = str_replace('&','*AND*',$row['org_name']); //echo "Ampersand found
"; + if (strpos($row['org_name'],'&')!==false) $row['org_name'] = str_replace('&','*AND*',$row['org_name']); $rows[$n]['id'] = 'org_name:'.$row['org_name']; foreach(array( 'org_unit' => lang('departments'), @@ -791,7 +787,7 @@ class Storage } else { - if (strpos($row[$by],'&')!==false) $row[$by] = str_replace('&','*AND*',$row[$by]); //echo "Ampersand found
"; + if (strpos($row[$by],'&')!==false) $row[$by] = str_replace('&','*AND*',$row[$by]); $rows[$n]['id'] .= '|||'.$by.':'.$row[$by]; } } @@ -890,13 +886,11 @@ class Storage $backend =& $this->get_backend($contact_id,$owner); $supported_fields = method_exists($backend,supported_fields) ? $backend->supported_fields() : $all_fields; - //echo "supported fields=";_debug_array($supported_fields); if ($type == 'supported') { return $supported_fields; } - //echo "unsupported fields=";_debug_array(array_diff($all_fields,$supported_fields)); return array_diff($all_fields,$supported_fields); } @@ -1015,10 +1009,9 @@ class Storage $lists[$list_id] = $data['list_name']; if ($data['list_owner'] != $this->user) { - $lists[$list_id] .= ' ('.$GLOBALS['egw']->common->grab_owner_name($data['list_owner']).')'; + $lists[$list_id] .= ' ('.Api\Accounts::username($data['list_owner']).')'; } } - //echo "

socontacts_sql::get_lists($required,'$extra_label')

\n"; _debug_array($lists); return $lists; } diff --git a/api/src/Contacts/Tracking.php b/api/src/Contacts/Tracking.php index a8643e9acb..44d8c47cc9 100644 --- a/api/src/Contacts/Tracking.php +++ b/api/src/Contacts/Tracking.php @@ -15,9 +15,6 @@ namespace EGroupware\Api\Contacts; use EGroupware\Api; -// explicitly reference classes still in phpgwapi -use common; - /** * Contacts history and notifications */ @@ -113,7 +110,6 @@ class Tracking extends Api\Storage\Tracking { unset($old); // not used, but required by function signature - //echo "

".__METHOD__."($name,".print_r($data,true).",...)

\n"; switch($name) { case 'copy': @@ -128,7 +124,6 @@ class Tracking extends Api\Storage\Tracking case 'sender': if ($data['is_contactform']) { - //echo "

addressbook_tracking::get_config($name,...) email={$data['email']}, n_given={$data['n_given']}, n_family={$data['n_family']}

\n"; return $data['email'] ? $data['n_given'].' '.$data['n_family'].' <'.$data['email'].'>' : null; } break; @@ -195,11 +190,11 @@ class Tracking extends Api\Storage\Tracking if (!$data['modified'] || !$old) { return lang('New contact submitted by %1 at %2', - common::grab_owner_name($data['creator']), + Api\Accounts::username($data['creator']), $this->datetime($data['created'])); } return lang('Contact modified by %1 at %2', - common::grab_owner_name($data['modifier']), + Api\Accounts::username($data['modifier']), $this->datetime($data['modified'])); } @@ -252,17 +247,16 @@ class Tracking extends Api\Storage\Tracking case 'bday': if ($data[$name]) { - list($y,$m,$d) = explode('-',$data[$name]); $details[$name] = array( 'label' => $label, - 'value' => common::dateformatorder($y,$m,$d,true), + 'value' => Api\DateTime::to($data[$name], true), ); } break; case 'owner': case 'creator': case 'modifier': $details[$name] = array( 'label' => $label, - 'value' => common::grab_owner_name($data[$name]), + 'value' => Api\Accounts::username($data[$name]), ); break; case 'cat_id': diff --git a/api/src/Session.php b/api/src/Session.php index fb36f7f8f9..b5d218cd0a 100644 --- a/api/src/Session.php +++ b/api/src/Session.php @@ -491,7 +491,6 @@ class Session } unset($GLOBALS['egw_info']['server']['default_domain']); // we kill this for security reasons - //echo "

session::create(login='$login'): lid='$this->account_lid', domain='$this->account_domain'

\n"; $user_ip = self::getuser_ip(); $this->account_id = $GLOBALS['egw']->accounts->name2id($this->account_lid,'account_lid','u'); @@ -533,8 +532,6 @@ class Session } $GLOBALS['egw_info']['user']['account_id'] = $this->account_id; - $GLOBALS['egw']->accounts->__construct(); - $GLOBALS['egw']->accounts->setAccountId($this->account_id); // for *DAV and eSync we use a pseudo sessionid created from md5(user:passwd) // --> allows this stateless protocolls which use basic auth to use sessions! @@ -619,7 +616,6 @@ class Session self::egw_setcookie('last_loginid', $this->account_lid ,$now+1209600); /* For 2 weeks */ self::egw_setcookie('last_domain',$this->account_domain,$now+1209600); } - //if (!$this->sessionid) echo "

session::create(login='$login') = '$this->sessionid': lid='$this->account_lid', domain='$this->account_domain'

\n"; if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check) successfull sessionid=$this->sessionid"); return $this->sessionid; @@ -1063,7 +1059,6 @@ class Session { if (self::ERROR_LOG_DEBUG) error_log(__METHOD__." ********* about to call session_destroy!"); session_unset(); - //echo '

'.__METHOD__.": session_destroy() returned ".(session_destroy() ? 'true' : 'false')."

\n"; @session_destroy(); // we need to (re-)load the eGW session-handler, as session_destroy unloads custom session-handlers if (function_exists('init_session_handler')) @@ -1295,7 +1290,6 @@ class Session { self::$cookie_path = '/'; } - //echo "

cookie_path='self::$cookie_path', cookie_domain='self::$cookie_domain'

\n"; session_set_cookie_params(0, self::$cookie_path, self::$cookie_domain, // if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true) @@ -1534,7 +1528,7 @@ class Session $password_md5 = $allow_password_md5 && preg_match('/^[a-f0-9]{32}$/',$password) ? $password : md5($password); $hash = sha1(strtolower($user).$password_md5); - //echo "

".__METHOD__."('$user','$password',$allow_password_md5) sha1('".strtolower($user)."$password_md5')='$hash'

\n"; + return $hash; } diff --git a/api/src/Storage/Customfields.php b/api/src/Storage/Customfields.php index c91847588f..f31579971d 100755 --- a/api/src/Storage/Customfields.php +++ b/api/src/Storage/Customfields.php @@ -15,7 +15,6 @@ namespace EGroupware\Api\Storage; use EGroupware\Api; // explicitly reference classes still in phpgwapi -use common; use egw_link; /** @@ -198,7 +197,7 @@ class Customfields implements \IteratorAggregate $values = array(); foreach($field['rows'] > 1 ? explode(',', $value) : (array) $value as $value) { - $values[] = common::grab_owner_name($value); + $values[] = Api\Accounts::username($value); } $value = implode(', ',$values); } diff --git a/phpgwapi/inc/class.accounts.inc.php b/phpgwapi/inc/class.accounts.inc.php index 972db72a63..78243a9cd1 100644 --- a/phpgwapi/inc/class.accounts.inc.php +++ b/phpgwapi/inc/class.accounts.inc.php @@ -16,6 +16,8 @@ * @version $Id$ */ +use EGroupware\Api; + /** * API - accounts * @@ -33,7 +35,7 @@ * * read and search return timestamps (account_(created|modified|lastlogin) in server-time! */ -class accounts +class accounts extends Api\Accounts { var $xmlrpc_methods = array( array( @@ -53,19 +55,6 @@ class accounts 'description' => 'Depricated: use search. Returns a list of accounts and/or groups' ), ); - /** - * Enables the session-cache, currently switched on independent of the backend - * - * @var boolean - */ - static $use_session_cache = true; - - /** - * Cache, stored in sesssion - * - * @var array - */ - static $cache; /** * Depricated: Account this class was instanciated for @@ -91,76 +80,6 @@ class accounts var $depricated_names = array('firstname','lastname','fullname','email','type', 'status','expires','lastlogin','lastloginfrom','lastpasswd_change'); - /** - * List of all config vars accounts depend on and therefore should be passed in when calling contructor with array syntax - * - * @var array - */ - static public $config_vars = array( - 'account_repository', 'auth_type', // auth_type if fallback if account_repository is not set - 'install_id', // instance-specific caching - 'auto_create_expire', 'default_group_lid', // auto-creation of accounts - 'ldap_host','ldap_root_dn','ldap_root_pw','ldap_context','ldap_group_context','ldap_search_filter', // ldap backend - 'ads_domain', 'ads_host', 'ads_admin_user', 'ads_admin_passwd', 'ads_connection', 'ads_context', // ads backend - ); - - /** - * Querytypes for the account-search - * - * @var array - */ - var $query_types = array( - 'all' => 'all fields', - 'firstname' => 'firstname', - 'lastname' => 'lastname', - 'lid' => 'LoginID', - 'email' => 'email', - 'start' => 'start with', - 'exact' => 'exact', - ); - - /** - * Backend to use - * - * @var accounts_sql|accounts_ldap - */ - var $backend; - - /** - * total number of found entries - * - * @var int - */ - var $total; - - /** - * Current configuration - * - * @var array - */ - var $config; - - /** - * hold an instance of the accounts class - * - * @var accounts the instance of the accounts class - */ - private static $_instance = NULL; - - /** - * Singleton - * - * @return accounts - */ - public static function getInstance() - { - if (self::$_instance === NULL) - { - self::$_instance = new accounts; - } - return self::$_instance; - } - /** * Constructor * @@ -170,40 +89,10 @@ class accounts { if (is_numeric($backend)) // depricated use with account_id { - if ((int)$backend) $this->account_id = (int) $backend; + if ((int)$backend) $this->setAccountId((int)$backend); $backend = null; } - if (is_array($backend)) - { - $this->config = $backend; - $backend = null; - self::$_instance = $this; // also set instance returned by singleton - self::$cache = array(); // and empty our internal (session) cache - } - else - { - $this->config =& $GLOBALS['egw_info']['server']; - - if (!isset(self::$_instance)) self::$_instance = $this; - } - if (is_null($backend)) - { - if (empty($this->config['account_repository'])) - { - if (!empty($this->config['auth_type'])) - { - $this->config['account_repository'] = $this->config['auth_type']; - } - else - { - $this->config['account_repository'] = 'sql'; - } - } - $backend = $this->config['account_repository']; - } - $backend_class = 'accounts_'.$backend; - - $this->backend = new $backend_class($this); + parent::__construct(); } /** @@ -220,953 +109,6 @@ class accounts } } - /** - * Searches / lists accounts: users and/or groups - * - * @param array with the following keys: - * @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both', - * 'groupmembers' (members of groups the user is a member of), 'groupmembers+memberships' (incl. memberships too) - * or integer group-id for a list of members of that group - * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set - * @param $param['order'] string column to sort after, default account_lid if unset - * @param $param['sort'] string 'ASC' or 'DESC', default 'ASC' if not set - * @param $param['query'] string to search for, no search if unset or empty - * @param $param['query_type'] string: - * 'all' - query all fields for containing $param[query] - * 'start' - query all fields starting with $param[query] - * 'exact' - query all fields for exact $param[query] - * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] - * @param $param['app'] string with an app-name, to limit result on accounts with run-right for that app - * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs - * @param $param['active']=true boolean - true: return only acctive accounts, false: return expired or deactivated too - * @return array with account_id => data pairs, data is an array with account_id, account_lid, account_firstname, - * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group - */ - function search($param) - { - //error_log(__METHOD__.'('.array2string($param).') '.function_backtrace()); - if (!isset($param['active'])) $param['active'] = true; // default is true = only return active accounts - - self::setup_cache(); - $account_search = &self::$cache['account_search']; - $serial = serialize($param); - - if (isset($account_search[$serial])) - { - $this->total = $account_search[$serial]['total']; - } - // no backend understands $param['app'], only sql understands type owngroups or groupmemember[+memberships] - // --> do an full search first and then filter and limit that search - elseif($param['app'] || $this->config['account_repository'] != 'sql' && - in_array($param['type'], array('owngroups','groupmembers','groupmembers+memberships'))) - { - $app = $param['app']; - unset($param['app']); - $start = $param['start']; - unset($param['start']); - $offset = $param['offset'] ? $param['offset'] : $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; - unset($param['offset']); - $stop = $start + $offset; - - if ($param['type'] == 'owngroups') - { - $members = $this->memberships($GLOBALS['egw_info']['user']['account_id'],true); - $param['type'] = 'groups'; - } - elseif(in_array($param['type'],array('groupmembers','groupmembers+memberships'))) - { - $members = array(); - foreach((array)$this->memberships($GLOBALS['egw_info']['user']['account_id'],true) as $grp) - { - $members = array_unique(array_merge($members, (array)$this->members($grp,true,$param['active']))); - if ($param['type'] == 'groupmembers+memberships') $members[] = $grp; - } - $param['type'] = $param['type'] == 'groupmembers+memberships' ? 'both' : 'accounts'; - } - // call ourself recursive to get (evtl. cached) full search - $full_search = $this->search($param); - - // filter search now on accounts with run-rights for app or a group - $valid = array(); - if ($app) - { - // we want the result merged, whatever it takes, as we only care for the ids - $valid = $this->split_accounts($app,!in_array($param['type'],array('accounts','groups')) ? 'merge' : $param['type'],$param['active']); - } - if (isset($members)) - { - //error_log(__METHOD__.'() members='.array2string($members)); - if (!$members) $members = array(); - $valid = !$app ? $members : array_intersect($valid,$members); // use the intersection - } - //error_log(__METHOD__."() limiting result to app='$app' and/or group=$group valid-ids=".array2string($valid)); - $n = 0; - $account_search[$serial]['data'] = array(); - foreach ($full_search as $id => $data) - { - if (!in_array($id,$valid)) - { - $this->total--; - continue; - } - // now we have a valid entry - if (!is_int($start) || $start <= $n && $n < $stop) - { - $account_search[$serial]['data'][$id] = $data; - } - $n++; - } - $account_search[$serial]['total'] = $this->total; - } - // direct search via backend - else - { - $account_search[$serial]['data'] = $this->backend->search($param); - if ($param['type'] !== 'accounts') - { - foreach($account_search[$serial]['data'] as &$account) - { - // add default description for Admins and Default group - if ($account['account_type'] === 'g' && empty($account['account_description'])) - { - self::add_default_group_description($account); - } - } - } - $account_search[$serial]['total'] = $this->total = $this->backend->total; - } - //echo "

accounts::search(".array2string(unserialize($serial)).")= returning ".count($account_search[$serial]['data'])." of $this->total entries

".print_r($account_search[$serial]['data'],True)."
\n"; - //echo "

accounts::search() end: ".microtime()."

\n"; - return $account_search[$serial]['data']; - } - - /** - * Query for accounts - * - * @param string|array $pattern - * @param array $options - * $options['filter']['group'] only return members of that group - * $options['account_type'] "accounts", "groups", "both" or "groupmembers" - * @return array with id - title pairs of the matching entries - */ - public static function link_query($pattern, array &$options = array()) - { - if (isset($options['filter']) && !is_array($options['filter'])) - { - $options['filter'] = (array)$options['filter']; - } - switch($GLOBALS['egw_info']['user']['preferences']['common']['account_display']) - { - case 'firstname': - case 'firstall': - $order = 'account_firstname,account_lastname'; - break; - case 'lastname': - case 'lastall': - $order = 'account_lastname,account_firstname'; - break; - default: - $order = 'account_lid'; - break; - } - $only_own = $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] === 'groupmembers' && - !isset($GLOBALS['egw_info']['user']['apps']['admin']); - switch($options['account_type']) - { - case 'accounts': - $type = $only_own ? 'groupmembers' : 'accounts'; - break; - case 'groups': - $type = $only_own ? 'owngroups' : 'groups'; - break; - case 'memberships': - $type = 'owngroups'; - break; - case 'owngroups': - case 'groupmembers': - $type = $options['account_type']; - break; - case 'both': - default: - $type = $only_own ? 'groupmembers+memberships' : 'both'; - break; - } - $accounts = array(); - foreach(self::getInstance()->search(array( - 'type' => $options['filter']['group'] < 0 ? $options['filter']['group'] : $type, - 'query' => $pattern, - 'query_type' => 'all', - 'order' => $order, - )) as $account) - { - $accounts[$account['account_id']] = common::display_fullname($account['account_lid'], - $account['account_firstname'],$account['account_lastname'],$account['account_id']); - } - return $accounts; - } - - /** - * Reads the data of one account - * - * It's depricated to use read with out parameter to read the internal data of this class!!! - * All key of the returned array use the 'account_' prefix. - * For backward compatibility some values are additionaly availible without the prefix, using them is depricated! - * - * @param int|string $id numeric account_id or string with account_lid (use of default value of 0 is depricated!!!) - * @param boolean $set_depricated_names =false set _additionaly_ the depricated keys without 'account_' prefix - * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found - */ - function read($id=0,$set_depricated_names=false) - { - if (!$id) // deprecated use!!! - { - return $this->data ? $this->data : $this->read_repository(); - } - if (!is_int($id) && !is_numeric($id)) - { - $id = $this->name2id($id); - } - if (!$id) return false; - - $data = self::cache_read($id); - - // add default description for Admins and Default group - if ($data['account_type'] === 'g' && empty($data['account_description'])) - { - self::add_default_group_description($data); - } - - if ($set_depricated_names && $data) - { - foreach($this->depricated_names as $name) - { - $data[$name] =& $data['account_'.$name]; - } - } - return $data; - } - - /** - * Get an account as json, returns only whitelisted fields: - * - 'account_id','account_lid','person_id','account_status', - * - 'account_firstname','account_lastname','account_email','account_fullname','account_phone' - * - * @param int|string $id - * @return string|boolean json or false if not found - */ - function json($id) - { - static $keys = array( - 'account_id','account_lid','person_id','account_status', - 'account_firstname','account_lastname','account_email','account_fullname','account_phone', - ); - if (($account = $this->read($id))) - { - $account = array_intersect_key($account, array_flip($keys)); - } - // for current user, add the apps available to him - if ($id == $GLOBALS['egw_info']['user']['account_id']) - { - foreach((array)$GLOBALS['egw_info']['user']['apps'] as $app => $data) - { - unset($data['table_defs']); // no need for that on the client - $account['apps'][$app] = $data; - } - } - return json_encode($account); - } - - /** - * Add a default description for stock groups: Admins, Default, NoGroup - * - * @param array &$data - */ - protected static function add_default_group_description(array &$data) - { - switch($data['account_lid']) - { - case 'Default': - $data['account_description'] = lang('EGroupware all users group, do NOT delete'); - break; - case 'Admins': - $data['account_description'] = lang('EGroupware administrators group, do NOT delete'); - break; - case 'NoGroup': - $data['account_description'] = lang('EGroupware anonymous users group, do NOT delete'); - break; - } - } - - /** - * Saves / adds the data of one account - * - * If no account_id is set in data the account is added and the new id is set in $data. - * - * @param array $data array with account-data - * @param boolean $check_depricated_names =false check _additionaly_ the depricated keys without 'account_' prefix - * @return int|boolean the account_id or false on error - */ - function save(&$data,$check_depricated_names=false) - { - if ($check_depricated_names) - { - foreach($this->depricated_names as $name) - { - if (isset($data[$name]) && !isset($data['account_'.$name])) - { - $data['account_'.$name] =& $data[$name]; - } - } - } - // add default description for Admins and Default group - if ($data['account_type'] === 'g' && empty($data['account_description'])) - { - self::add_default_group_description($data); - } - if (($id = $this->backend->save($data)) && $data['account_type'] != 'g') - { - // if we are not on a pure LDAP system, we have to write the account-date via the contacts class now - if (($this->config['account_repository'] == 'sql' || $this->config['contact_repository'] == 'sql-ldap') && - (!($old = $this->read($data['account_id'])) || // only for new account or changed contact-data - $old['account_firstname'] != $data['account_firstname'] || - $old['account_lastname'] != $data['account_lastname'] || - $old['account_email'] != $data['account_email'])) - { - if (!$data['person_id']) $data['person_id'] = $old['person_id']; - - $contact = array( - 'n_given' => $data['account_firstname'], - 'n_family' => $data['account_lastname'], - 'email' => $data['account_email'], - 'account_id' => $data['account_id'], - 'id' => $data['person_id'], - 'owner' => 0, - ); - $GLOBALS['egw']->contacts->save($contact,true); // true = ignore addressbook acl - } - // save primary group if necessary - if ($data['account_primary_group'] && (!($memberships = $this->memberships($id,true)) || - !in_array($data['account_primary_group'],$memberships))) - { - $memberships[] = $data['account_primary_group']; - $this->set_memberships($memberships, $id); // invalidates cache for account_id and primary group - } - } - // as some backends set (group-)members in save, we need to invalidate their members too! - $invalidate = isset($data['account_members']) ? $data['account_members'] : array(); - $invalidate[] = $data['account_id']; - self::cache_invalidate($invalidate); - - return $id; - } - - /** - * Delete one account, deletes also all acl-entries for that account - * - * @param int|string $id numeric account_id or string with account_lid - * @return boolean true on success, false otherwise - */ - function delete($id) - { - if (!is_int($id) && !is_numeric($id)) - { - $id = $this->name2id($id); - } - if (!$id) return false; - - if ($this->get_type($id) == 'u') - { - $invalidate = $this->memberships($id, true); - } - else - { - $invalidate = $this->members($id, true, false); - } - $invalidate[] = $id; - - $this->backend->delete($id); - - self::cache_invalidate($invalidate); - - // delete all acl_entries belonging to that user or group - $GLOBALS['egw']->acl->delete_account($id); - - // delete all categories belonging to that user or group - categories::delete_account($id); - - return true; - } - - /** - * test if an account is expired - * - * Can be used static if array with user-data is supplied - * - * @param array $data =null array with account data, not specifying the account is depricated!!! - * @return boolean true=expired (no more login possible), false otherwise - */ - function is_expired($data=null) - { - if (is_null($data)) $data = $this->data; // depricated use - - $expires = isset($data['account_expires']) ? $data['account_expires'] : $data['expires']; - - return $expires != -1 && $expires < time(); - } - - /** - * Test if an account is active - NOT deactivated or expired - * - * Can be used static if array with user-data is supplied - * - * @param int|array $data account_id or array with account-data - * @return boolean false if account does not exist, is expired or decativated, true otherwise - */ - function is_active($data) - { - if (!is_array($data)) $data = $this->read($data); - - return $data && !(self::is_expired($data) || $data['account_status'] != 'A'); - } - - /** - * convert an alphanumeric account-value (account_lid, account_email) to the account_id - * - * Please note: - * - if a group and an user have the same account_lid the group will be returned (LDAP only) - * - if multiple user have the same email address, the returned user is undefined - * - * @param string $name value to convert - * @param string $which ='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname - * @param string $account_type =null u = user or g = group, or default null = try both - * @return int|false numeric account_id or false on error ($name not found) - */ - function name2id($name,$which='account_lid',$account_type=null) - { - // Don't bother searching for empty or non-scalar account_lid - if(empty($name) || !is_scalar($name)) - { - return False; - } - - self::setup_cache(); - $name_list = &self::$cache['name_list']; - - if(@isset($name_list[$which][$name]) && $name_list[$which][$name]) - { - return $name_list[$which][$name]; - } - - return $name_list[$which][$name] = $this->backend->name2id($name,$which,$account_type); - } - - /** - * Convert an numeric account_id to any other value of that account (account_lid, account_email, ...) - * - * Uses the read method to fetch all data. - * - * @param int|string $account_id numeric account_id or account_lid - * @param string $which ='account_lid' type to convert to: account_lid (default), account_email, ... - * @return string|boolean converted value or false on error ($account_id not found) - */ - static function id2name($account_id, $which='account_lid') - { - if (!is_numeric($account_id) && !($account_id = self::getInstance()->name2id($account_id))) - { - return false; - } - try { - if (!($data = self::cache_read($account_id))) return false; - } - catch (Exception $e) { - unset($e); - return false; - } - //echo "

accounts::id2name($account_id,$which)='{$data[$which]}'"; - return $data[$which]; - } - - /** - * get the type of an account: 'u' = user, 'g' = group - * - * @param int|string $account_id numeric account-id or alphanum. account-lid, - * if !$accountid account of the user of this session - * @return string/false 'u' = user, 'g' = group or false on error ($accountid not found) - */ - function get_type($account_id) - { - if (!is_int($account_id) && !is_numeric($account_id)) - { - $account_id = $this->name2id($account_id); - } - return $account_id > 0 ? 'u' : ($account_id < 0 ? 'g' : false); - } - - /** - * check if an account exists and if it is an user or group - * - * @param int|string $account_id numeric account_id or account_lid - * @return int 0 = acount does not exist, 1 = user, 2 = group - */ - function exists($account_id) - { - if (!($data = $this->read($account_id))) - { - return 0; - } - return $data['account_type'] == 'u' ? 1 : 2; - } - - /** - * Checks if a given account is visible to current user - * - * Not all existing accounts are visible because off account_selection preference: 'none' or 'groupmembers' - * - * @param int|string $account_id nummeric account_id or account_lid - * @return boolean true = account is visible, false = account not visible, null = account does not exist - */ - function visible($account_id) - { - if (!is_numeric($account_id)) // account_lid given - { - $account_lid = $account_id; - if (!($account_id = $this->name2id($account_lid))) return null; - } - else - { - if (!($account_lid = $this->id2name($account_id))) return null; - } - if (!isset($GLOBALS['egw_info']['user']['apps']['admin']) && - // do NOT allow other user, if account-selection is none - ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'none' && - $account_lid != $GLOBALS['egw_info']['user']['account_lid'] || - // only allow group-members for account-selection is groupmembers - $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'groupmembers' && - !array_intersect((array)$this->memberships($account_id,true), - (array)$this->memberships($GLOBALS['egw_info']['user']['account_id'],true)))) - { - //error_log(__METHOD__."($account_id='$account_lid') returning FALSE"); - return false; // user is not allowed to see given account - } - return true; // user allowed to see given account - } - - /** - * Get all memberships of an account $account_id / groups the account is a member off - * - * @param int|string $account_id numeric account-id or alphanum. account-lid - * @param boolean $just_id =false return just account_id's or account_id => account_lid pairs - * @return array with account_id's ($just_id) or account_id => account_lid pairs (!$just_id) - */ - function memberships($account_id, $just_id=false) - { - if (!is_int($account_id) && !is_numeric($account_id)) - { - $account_id = $this->name2id($account_id,'account_lid','u'); - } - if ($account_id && ($data = self::cache_read($account_id))) - { - $ret = $just_id && $data['memberships'] ? array_keys($data['memberships']) : $data['memberships']; - } - //error_log(__METHOD__."($account_id, $just_id) data=".array2string($data)." returning ".array2string($ret)); - return $ret; - } - - /** - * Sets the memberships of a given account - * - * @param array $groups array with gidnumbers - * @param int $account_id uidnumber - */ - function set_memberships($groups,$account_id) - { - //echo "

accounts::set_memberships(".print_r($groups,true).",$account_id)

\n"; - if (!is_int($account_id) && !is_numeric($account_id)) - { - $account_id = $this->name2id($account_id); - } - if (($old_memberships = $this->memberships($account_id, true)) != $groups) - { - $this->backend->set_memberships($groups, $account_id); - - if (!$old_memberships) $old_memberships = array(); - self::cache_invalidate(array_unique(array_merge( - array($account_id), - array_diff($old_memberships, $groups), - array_diff($groups, $old_memberships) - ))); - } - } - - /** - * Get all members of the group $account_id - * - * @param int|string $account_id ='' numeric account-id or alphanum. account-lid, - * default account of the user of this session - * @param boolean $just_id =false return just an array of id's and not id => lid pairs, default false - * @param boolean $active =false true: return only active (not expired or deactived) members, false: return all accounts - * @return array with account_id ($just_id) or account_id => account_lid pairs (!$just_id) - */ - function members($account_id, $just_id=false, $active=true) - { - if (!is_int($account_id) && !is_numeric($account_id)) - { - $account_id = $this->name2id($account_id); - } - if ($account_id && ($data = self::cache_read($account_id, $active))) - { - $members = $active ? $data['members-active'] : $data['members']; - - return $just_id && $members ? array_keys($members) : $members; - } - return null; - } - - /** - * Set the members of a group - * - * @param array $members array with uidnumber or uid's - * @param int $gid gidnumber of group to set - */ - function set_members($members,$gid) - { - //echo "

accounts::set_members(".print_r($members,true).",$gid)

\n"; - if (($old_members = $this->members($gid, true, false)) != $members) - { - $this->backend->set_members($members, $gid); - - self::cache_invalidate(array_unique(array_merge( - array($gid), - array_diff($old_members, $members), - array_diff($members, $old_members) - ))); - } - } - - /** - * splits users and groups from a array of id's or the accounts with run-rights for a given app-name - * - * @param array $app_users array of user-id's or app-name (if you use app-name the result gets cached!) - * @param string $use what should be returned only an array with id's of either 'accounts' or 'groups'. - * Or an array with arrays for 'both' under the keys 'groups' and 'accounts' or 'merge' for accounts - * and groups merged into one array - * @param boolean $active =false true: return only active (not expired or deactived) members, false: return all accounts - * @return array/boolean see $use, false on error (wront $use) - */ - function split_accounts($app_users,$use='both',$active=true) - { - if (!is_array($app_users)) - { - self::setup_cache(); - $cache = &self::$cache['account_split'][$app_user]; - - if (is_array($cache)) - { - return $cache; - } - $app_users = $GLOBALS['egw']->acl->get_ids_for_location('run',1,$app_users); - } - $accounts = array( - 'accounts' => array(), - 'groups' => array(), - ); - foreach($app_users as $id) - { - $type = $this->get_type($id); - if($type == 'g') - { - $accounts['groups'][$id] = $id; - if ($use != 'groups') - { - foreach((array)$this->members($id, true, $active) as $id) - { - $accounts['accounts'][$id] = $id; - } - } - } - else - { - $accounts['accounts'][$id] = $id; - } - } - - // not sure why they need to be sorted, but we need to remove the keys anyway - sort($accounts['groups']); - sort($accounts['accounts']); - - if (isset($cache)) - { - $cache = $accounts; - } - //echo "

accounts::split_accounts(".print_r($app_users,True).",'$use') =

".print_r($accounts,True)."
\n"; - - switch($use) - { - case 'both': - return $accounts; - case 'groups': - return $accounts['groups']; - case 'accounts': - return $accounts['accounts']; - case 'merge': - return array_merge($accounts['accounts'],$accounts['groups']); - } - return False; - } - - /** - * Add an account for an authenticated user - * - * Expiration date and primary group are read from the system configuration. - * - * @param string $account_lid - * @param string $passwd - * @param array $GLOBALS['auto_create_acct'] values for 'firstname', 'lastname', 'email' and 'primary_group' - * @return int|boolean account_id or false on error - */ - function auto_add($account_lid, $passwd) - { - $expires = !isset($this->config['auto_create_expire']) || - $this->config['auto_create_expire'] == 'never' ? -1 : - time() + $this->config['auto_create_expire'] + 2; - - $memberships = array(); - $default_group_id = null; - // check if we have a comma or semicolon delimited list of groups --> add first as primary and rest as memberships - foreach(preg_split('/[,;] */',$this->config['default_group_lid']) as $group_lid) - { - if (($group_id = $this->name2id($group_lid,'account_lid','g'))) - { - if (!$default_group_id) $default_group_id = $group_id; - $memberships[] = $group_id; - } - } - if (!$default_group_id && ($default_group_id = $this->name2id('Default','account_lid','g'))) - { - $memberships[] = $default_group_id; - } - - $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && - $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) === 'g' ? - (int)$GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; - if ($primary_group && !in_array($primary_group, $memberships)) - { - $memberships[] = $primary_group; - } - $data = array( - 'account_lid' => $account_lid, - 'account_type' => 'u', - 'account_passwd' => $passwd, - 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', - 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', - 'account_email' => $GLOBALS['auto_create_acct']['email'], - 'account_status' => 'A', - 'account_expires' => $expires, - 'account_primary_group' => $primary_group, - ); - // use given account_id, if it's not already used - if (isset($GLOBALS['auto_create_acct']['account_id']) && - is_numeric($GLOBALS['auto_create_acct']['account_id']) && - !$this->id2name($GLOBALS['auto_create_acct']['account_id'])) - { - $data['account_id'] = $GLOBALS['auto_create_acct']['account_id']; - } - if (!($data['account_id'] = $this->save($data))) - { - return false; - } - // set memberships if given - if ($memberships) - { - $this->set_memberships($memberships,$data['account_id']); - } - // set the appropriate value for the can change password flag (assume users can, if the admin requires users to change their password) - $data['changepassword'] = (bool)$GLOBALS['egw_info']['server']['change_pwd_every_x_days']; - if(!$data['changepassword']) - { - $GLOBALS['egw']->acl->add_repository('preferences','nopasswordchange',$data['account_id'],1); - } - else - { - $GLOBALS['egw']->acl->delete_repository('preferences','nopasswordchange',$data['account_id']); - } - // call hook to notify interested apps about the new account - $GLOBALS['hook_values'] = $data; - $GLOBALS['egw']->hooks->process($data+array( - 'location' => 'addaccount', - // at login-time only the hooks from the following apps will be called - 'order' => array('felamimail','fudforum'), - ),False,True); // called for every app now, not only enabled ones - unset($data['changepassword']); - - return $data['account_id']; - } - - /** - * Update the last login timestamps and the IP - * - * @param int $account_id - * @param string $ip - * @return int lastlogin time - */ - function update_lastlogin($account_id, $ip) - { - return $this->backend->update_lastlogin($account_id, $ip); - } - - /** - * Query if backend allows to change username aka account_lid - * - * @return boolean false if backend does NOT allow it (AD), true otherwise (SQL, LDAP) - */ - function change_account_lid_allowed() - { - $change_account_lid = constant(get_class($this->backend).'::CHANGE_ACCOUNT_LID'); - if (!isset($change_account_lid)) $change_account_lid = true; - return $change_account_lid; - } - - /** - * Query if backend requires password to be set, before allowing to enable an account - * - * @return boolean true if backend requires a password (AD), false or null otherwise (SQL, LDAP) - */ - function require_password_for_enable() - { - return constant(get_class($this->backend).'::REQUIRE_PASSWORD_FOR_ENABLE'); - } - - /** - * Invalidate cache (or parts of it) after change in $account_ids - * - * We use now an instance-wide read-cache storing account-data and members(hips). - * - * @param int|array $account_ids user- or group-id(s) for which cache should be invalidated, default 0 = only search/name2id cache - */ - static function cache_invalidate($account_ids=0) - { - //error_log(__METHOD__.'('.array2string($account_ids).')'); - - // instance-wide cache - if ($account_ids) - { - foreach((array)$account_ids as $account_id) - { - $instance = self::getInstance(); - - egw_cache::unsetCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id); - - unset(self::$request_cache[$account_id]); - } - } - - // session-cache - if (self::$cache) self::$cache = array(); - egw_cache::unsetSession('accounts_cache','phpgwapi'); - - if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited - { - egw::invalidate_session_cache(); // invalidates whole egw-enviroment if stored in the session - } - } - - /** - * Timeout of instance wide cache for reading account-data and members(hips) - */ - const READ_CACHE_TIMEOUT = 43200; - - /** - * Local per request cache, to minimize calls to instance cache - * - * @var array - */ - static $request_cache = array(); - - /** - * Read account incl. members/memberships from cache (or backend and cache it) - * - * @param int $account_id - * @param boolean $need_active =false true = 'members-active' required - * @return array - * @throws egw_exception_wrong_parameter if no integer was passed as $account_id - */ - static function cache_read($account_id, $need_active=false) - { - if (!is_numeric($account_id)) throw new egw_exception_wrong_parameter('Not an integer!'); - - $account =& self::$request_cache[$account_id]; - - if (!isset($account)) // not in request cache --> try instance cache - { - $instance = self::getInstance(); - - $account = egw_cache::getCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id); - - if (!isset($account)) // not in instance cache --> read from backend - { - if (($account = $instance->backend->read($account_id))) - { - if ($instance->get_type($account_id) == 'u') - { - if (!isset($account['memberships'])) $account['memberships'] = $instance->backend->memberships($account_id); - } - else - { - if (!isset($account['members'])) $account['members'] = $instance->backend->members($account_id); - } - egw_cache::setCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id, $account, self::READ_CACHE_TIMEOUT); - } - //error_log(__METHOD__."($account_id) read from backend ".array2string($account)); - } - //else error_log(__METHOD__."($account_id) read from instance cache ".array2string($account)); - } - // if required and not already set, query active members AND cache them too - if ($need_active && $account_id < 0 && !isset($account['members-active'])) - { - $instance = self::getInstance(); - $account['members-active'] = array(); - foreach((array)$account['members'] as $id => $lid) - { - if ($instance->is_active($id)) $account['members-active'][$id] = $lid; - } - egw_cache::setCache($instance->config['install_id'], __CLASS__, 'account-'.$account_id, $account, self::READ_CACHE_TIMEOUT); - } - //error_log(__METHOD__."($account_id, $need_active) returning ".array2string($account)); - return $account; - } - - /** - * Internal functions not meant to use outside this class!!! - */ - - /** - * Sets up session cache, now only used for search and name2id list - * - * Other account-data is cached on instance-level - * - * The cache is shared between all instances of the account-class and it can be save in the session, - * if use_session_cache is set to True - * - * @internal - */ - private static function setup_cache() - { - if (is_array(self::$cache)) return; // cache is already setup - - if (self::$use_session_cache && is_object($GLOBALS['egw']->session)) - { - self::$cache =& egw_cache::getSession('accounts_cache','phpgwapi'); - //echo "

restoring cache from session, ".count(call_user_func_array('array_merge',(array)self::$cache))." items

\n"; - } - //error_log(__METHOD__."() use_session_cache=".array2string(self::$use_session_cache).", is_array(self::\$cache)=".array2string(is_array(self::$cache))); - - if (!is_array(self::$cache)) - { - //echo "

initialising this->cache to array()

\n"; - self::$cache = array(); - } - } - /** * @deprecated not used any more, as static cache is a reference to the session */ @@ -1271,7 +213,6 @@ class accounts { $old[] = array('account_id' => $id, 'account_name' => $lid); } - //echo "

accounts::membership($accountid)="; _debug_array($old); return $old; } diff --git a/phpgwapi/inc/class.auth_ads.inc.php b/phpgwapi/inc/class.auth_ads.inc.php index da36f35e48..82a7bf35dd 100644 --- a/phpgwapi/inc/class.auth_ads.inc.php +++ b/phpgwapi/inc/class.auth_ads.inc.php @@ -159,8 +159,8 @@ class auth_ads implements auth_backend if ($lastpwdchange) { // Samba4 can NOT set -1 for current time - $ldapServerInfo = ldapserverinfo::get($adldap->getLdapConnection(), $GLOBALS['egw_info']['server']['ads_host']); - if ($ldapServerInfo->serverType == SAMBA4_LDAPSERVER) + $ldapServerInfo = Api\Ldap\ServerInfo::get($adldap->getLdapConnection(), $GLOBALS['egw_info']['server']['ads_host']); + if ($ldapServerInfo->serverType == Api\Ldap\ServerInfo::SAMBA4) { if ($lastpwdchange == -1) $lastpwdchange = time(); } diff --git a/phpgwapi/inc/class.common.inc.php b/phpgwapi/inc/class.common.inc.php index 044bf0fc2f..247cac1f91 100644 --- a/phpgwapi/inc/class.common.inc.php +++ b/phpgwapi/inc/class.common.inc.php @@ -238,19 +238,19 @@ class common * It's actually a PHP-Bug, that we have to escape space. * For all other Characters, refer to RFC2254. * - * @deprecated use ldap::quote() + * @deprecated use Api\Ldap::quote() * @param $string either a string to be escaped, or an array of values to be escaped * @return string */ static function ldap_addslashes($string='') { - return ldap::quote($string); + return Api\Ldap::quote($string); } /** * connect to the ldap server and return a handle * - * @deprecated use ldap::ldapConnect() + * @deprecated use Api\Ldap::ldapConnect() * @param $host ldap host * @param $dn ldap_root_dn * @param $passwd ldap_root_pw @@ -342,76 +342,23 @@ class common * @param $firstname ='' firstname * @param $lastname ='' lastname * @param $accountid =0 id, to check if it's a user or group, otherwise the lid will be used + * @deprecated use Api\Accounts::format_username() */ static function display_fullname($lid = '', $firstname = '', $lastname = '',$accountid=0) { - if (! $lid && ! $firstname && ! $lastname) - { - $lid = $GLOBALS['egw_info']['user']['account_lid']; - $firstname = $GLOBALS['egw_info']['user']['account_firstname']; - $lastname = $GLOBALS['egw_info']['user']['account_lastname']; - } - $is_group = $GLOBALS['egw']->accounts->get_type($accountid ? $accountid : $lid) == 'g'; - - if (empty($firstname)) $firstname = $lid; - if (empty($lastname) || $is_group) - { - $lastname = $is_group ? lang('Group') : lang('User'); - } - $display = $GLOBALS['egw_info']['user']['preferences']['common']['account_display']; - - if ($firstname && $lastname) - { - $delimiter = $is_group ? ' ' : ', '; - } - else - { - $delimiter = ''; - } - - $name = ''; - switch($display) - { - case 'firstname': - $name = $firstname . ' ' . $lastname; - break; - case 'lastname': - $name = $lastname . $delimiter . $firstname; - break; - case 'username': - $name = $lid; - break; - case 'firstall': - $name = $firstname . ' ' . $lastname . ' ['.$lid.']'; - break; - case 'lastall': - $name = $lastname . $delimiter . $firstname . ' ['.$lid.']'; - break; - case 'allfirst': - $name = '['.$lid.'] ' . $firstname . ' ' . $lastname; - break; - case 'all': - /* fall through */ - default: - $name = '['.$lid.'] ' . $lastname . $delimiter . $firstname; - } - return $name; + return Api\Accounts::format_username($lid, $firstname, $lastname, $accountid); } /** - * grab the owner name + * Return formatted username for a given account_id * * @param string $accountid =null account id * @return string full name of user or "#$accountid" if user not found + * @deprecated use Api\Accounts::username($accountid) */ static function grab_owner_name($accountid=null) { - $lid = $fname = $lname = null; - if (!$GLOBALS['egw']->accounts->get_account_name($accountid,$lid,$fname,$lname)) - { - return '#'.$accountid; - } - return self::display_fullname($lid,$fname,$lname,$accountid); + return Api\Accounts::username($accountid); } /** diff --git a/phpgwapi/inc/class.contacts.inc.php b/phpgwapi/inc/class.contacts.inc.php index d04f2cce06..494071fc22 100644 --- a/phpgwapi/inc/class.contacts.inc.php +++ b/phpgwapi/inc/class.contacts.inc.php @@ -5,15 +5,17 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package api - * @copyright (c) 2006-8 by Ralf Becker + * @copyright (c) 2006-16 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ +use EGroupware\Api; + /** * contacts service provided by the addressbook application */ -class contacts extends addressbook_bo +class contacts extends Api\Contacts { /** * @deprecated since 1.3 use total @@ -40,10 +42,10 @@ class contacts extends addressbook_bo /** * reads contacts matched by key and puts all cols in the data array * - * reimplemented from bocontacts to call the old read function, if more then one param + * reimplemented to call the old read function, if more then one param * - * @param int/string $contact_id - * @return array/boolean contact data or false on error + * @param int|string $contact_id + * @return array|boolean contact data or false on error */ function read($contact_id) { @@ -67,15 +69,15 @@ class contacts extends addressbook_bo * This method was named read in eGW 1.0 and 1.2 * * @deprecated since 1.3 use search() instead - * @param int $start=0 starting number of the range, if $limit != 0 - * @param int $limit=0 max. number of entries to return, 0=all - * @param array $fields=null fields to return or null for all stock fields, fields are either in the keys or values - * @param string $query='' search pattern or '' for none - * @param string $filter='' filters with syntax like =,=,=!'' for not empty - * @param string $sort='' sorting: ASC or DESC - * @param string $order='' column to order, default ('') n_family,n_given,email ASC - * @param int $lastmod=-1 return only values modified after given timestamp, default (-1) return all - * @param string $cquery='' return only entries starting with given character, default ('') all + * @param int $start =0 starting number of the range, if $limit != 0 + * @param int $limit =0 max. number of entries to return, 0=all + * @param array $fields =null fields to return or null for all stock fields, fields are either in the keys or values + * @param string $query ='' search pattern or '' for none + * @param string $filter ='' filters with syntax like =,=,=!'' for not empty + * @param string $sort ='' sorting: ASC or DESC + * @param string $order ='' column to order, default ('') n_family,n_given,email ASC + * @param int $lastmod =-1 return only values modified after given timestamp, default (-1) return all + * @param string $cquery ='' return only entries starting with given character, default ('') all * @return array of contacts */ function old_read($start=0,$limit=0,$fields=null,$query='',$filter='',$sort='',$order='', $lastmod=-1,$cquery='') @@ -155,7 +157,7 @@ class contacts extends addressbook_bo $row['bday'] = egw_time::to((isset($row['bday'])?$row['bday']:$row['contact_bday']),"Y-m-d"); list($y,$m,$d) = explode('-',$row['bday']); $rows[$n]['bday'] = sprintf('%d/%d/%04d',$m,$d,$y); - + } } } @@ -168,11 +170,13 @@ class contacts extends addressbook_bo * * @deprecated since 1.3 use read() instead * @param int $id - * @param string $fields='' we always return all fields now + * @param string $fields ='' we always return all fields now * @return array/boolean contact or false on failure */ function read_single_entry($id,$fields='') { + unset($fields); // not used, but required by function signature + return $this->read($id); } @@ -182,9 +186,9 @@ class contacts extends addressbook_bo * @deprecated since 1.3 use save() instead * @param int $owner owner of the entry * @param array $fields contains access, cat_id and tif if their param is null - * @param string $access=null 'private' or 'public' - * @param int $cat_id=null - * @param string $tid=null 'n' + * @param string $access =null 'private' or 'public' + * @param int $cat_id =null + * @param string $tid =null 'n' * @return array/boolean contact or false on failure */ function add($owner,$fields,$access=NULL,$cat_id=NULL,$tid=NULL) @@ -214,10 +218,10 @@ class contacts extends addressbook_bo * @param int $id id of the entry * @param int $owner owner of the entry * @param array $fields contains access, cat_id and tif if their param is null - * @param string $access=null 'private' or 'public' - * @param int $cat_id=null - * @param string $tid=null 'n' - * @return array/boolean contact or false on failure + * @param string $access =null 'private' or 'public' + * @param int $cat_id =null + * @param string $tid =null 'n' + * @return array|boolean contact or false on failure */ function update($id,$owner,$fields,$access=NULL,$cat_id=NULL,$tid=NULL) { diff --git a/phpgwapi/inc/class.egw.inc.php b/phpgwapi/inc/class.egw.inc.php index aa0939a9c2..cc99a081be 100644 --- a/phpgwapi/inc/class.egw.inc.php +++ b/phpgwapi/inc/class.egw.inc.php @@ -665,10 +665,11 @@ class egw_minimal // 'js' => 'javascript', 'link' => 'bolink', // depricated use static egw_link methods 'datetime' => 'egw_datetime', -// 'session' => 'sessions', - 'session' => 'egw_session', 'framework' => true, // special handling in __get() 'template' => 'Template', + // classes moved to new api dir + 'session' => 'EGroupware\Api\Session', + 'ldap' => 'EGroupware\Api\Ldap', ); /** diff --git a/phpgwapi/inc/class.egw_customfields.inc.php b/phpgwapi/inc/class.egw_customfields.inc.php index 4f44c49f59..40573b4618 100755 --- a/phpgwapi/inc/class.egw_customfields.inc.php +++ b/phpgwapi/inc/class.egw_customfields.inc.php @@ -15,6 +15,6 @@ use EGroupware\Api; /** * Managing custom-field definitions * - * @deprecated use Api\Customfields + * @deprecated use Api\Storage\Customfields */ -class egw_customfields extends Api\Customfields { } +class egw_customfields extends Api\Storage\Customfields { } diff --git a/phpgwapi/inc/class.historylog.inc.php b/phpgwapi/inc/class.historylog.inc.php index e36903dc19..28cede8569 100644 --- a/phpgwapi/inc/class.historylog.inc.php +++ b/phpgwapi/inc/class.historylog.inc.php @@ -14,281 +14,19 @@ * @version $Id$ */ +use EGroupware\Api; + /** * Record history logging service * * This class need to be instanciated for EACH app, which wishes to use it! */ -class historylog +class historylog extends Api\Storage\History { - /** - * Reference to the global db object - * - * @var egw_db - */ - var $db; - const TABLE = 'egw_history_log'; - /** - * App.name this class is instanciated for / working on - * - * @var string - */ - var $appname; - /** - * offset in secconds between user and server-time, - * it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time - * - * @var int - */ - var $tz_offset_s; - var $user; var $template; var $nextmatchs; - var $types = array( - 'C' => 'Created', - 'D' => 'Deleted', - 'E' => 'Edited' - ); var $alternate_handlers = array(); - /** - * 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; - } - $this->tz_offset_s = $GLOBALS['egw']->datetime->tz_offset; - } - - /** - * 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[] = egw_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 - */ - 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 = egw_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 = sqlfs_stream_wrapper::url_stat("/apps/{$query['appname']}/{$query['record_id']}",STREAM_URL_STAT_LINK)) - { - $_query[] = array( - 'table' => sqlfs_stream_wrapper::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],bo_tracking::ONE2N_SEPERATOR) !== false) - { - $row[$field] = explode(bo_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 = sqlfs_stream_wrapper::id2path($row['history_id']); - - // Apparently we don't have to do anything with it, just ask... - // without this, previous versions are not handled properly - egw_vfs::getExtraInfo($path); - - $row['history_new_value'] = array( - 'path' => $path, - 'name' => 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[] = egw_db::strip_array_keys($row,'history_'); - } - if ($mysql_calc_rows) - { - $total = $GLOBALS['egw']->db->query('SELECT FOUND_ROWS()')->fetchColumn(); - } - - return $total; - } - /** * return history-log for one record of $this->appname * @@ -363,8 +101,8 @@ class historylog */ function return_html($filter_out,$orderby,$sort, $record_id) { - $this->template =& CreateObject('phpgwapi.Template',EGW_TEMPLATE_DIR); - $this->nextmatchs =& CreateObject('phpgwapi.nextmatchs'); + $this->template = new Template(EGW_TEMPLATE_DIR); + $this->nextmatchs = new nextmatchs(); $this->template->set_file('_history','history_list.tpl'); @@ -398,7 +136,7 @@ class historylog { $this->nextmatchs->template_alternate_row_color($this->template); - $this->template->set_var('row_date',$GLOBALS['egw']->common->show_date($value['datetime'])); + $this->template->set_var('row_date',common::show_date($value['datetime'])); $this->template->set_var('row_owner',$value['owner']); if ($this->alternate_handlers[$value['status']]) diff --git a/phpgwapi/inc/class.ldap.inc.php b/phpgwapi/inc/class.ldap.inc.php deleted file mode 100644 index 010c4703b1..0000000000 --- a/phpgwapi/inc/class.ldap.inc.php +++ /dev/null @@ -1,261 +0,0 @@ - - * @author Ralf Becker - * - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package api - * @subpackage ldap - * @version $Id$ - */ - -/** - * 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 ldapserverinfo $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 ldapserverinfo|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 egw_exception_assertion_failed 'LDAP support unavailable!' (no ldap extension) - */ - function ldapConnect($host='', $dn='', $passwd='') - { - if(!function_exists('ldap_connect')) - { - if ($this->exception_on_error) throw new egw_exception_assertion_failed('LDAP support unavailable!'); - - printf('Error: LDAP support unavailable
',$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 egw_exception_no_permission("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,'ldapserverinfo') || - $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 = ldapserverinfo::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 = (array) unserialize($GLOBALS['egw']->session->appsession('ldapServerInfo')); - } - } - - /** - * save the session data - */ - function saveSessionData() - { - if (isset($GLOBALS['egw']->session)) // no availible in setup - { - $GLOBALS['egw']->session->appsession('ldapServerInfo','',serialize($this->ldapServerInfo)); - } - } -} diff --git a/phpgwapi/inc/class.ldapserverinfo.inc.php b/phpgwapi/inc/class.ldapserverinfo.inc.php deleted file mode 100644 index 336b207b9c..0000000000 --- a/phpgwapi/inc/class.ldapserverinfo.inc.php +++ /dev/null @@ -1,232 +0,0 @@ - - * - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package api - * @subpackage ldap - * @version $Id$ - */ - -define('UNKNOWN_LDAPSERVER',0); -define('OPENLDAP_LDAPSERVER',1); -define('SAMBA4_LDAPSERVER',2); - -/** - * Class to store and retrieve information (eg. supported object classes) of a connected ldap server - */ -class ldapserverinfo -{ - /** - * @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 ldapserverinfo($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++) - { - $pattern = '/^\( (.*) NAME \'(\w*)\' /'; - if(preg_match($pattern, $info[0]['objectclasses'][$i], $matches)) - { - #_debug_array($matches); - if(count($matches) == 3) - { - $supportedObjectClasses[$matches[1]] = strtolower($matches[2]); - } - } - } - $ldapServerInfo->setSupportedObjectClasses($supportedObjectClasses); - } - } - } - } - } - } - return $ldapServerInfo; - } -} diff --git a/phpgwapi/inc/class.preferences.inc.php b/phpgwapi/inc/class.preferences.inc.php index 166dec971c..c6fe4b14cf 100644 --- a/phpgwapi/inc/class.preferences.inc.php +++ b/phpgwapi/inc/class.preferences.inc.php @@ -12,6 +12,8 @@ * @version $Id$ */ +use EGroupware\Api; + /** * preferences class used for setting application preferences * @@ -278,14 +280,11 @@ class preferences { $GLOBALS['egw_info']['user']['preferences'] = $this->data; // else no lang() } - // we cant use phpgw_info/user/fullname, as it's not set when we run - $lid = $fname = $lname = null; - $GLOBALS['egw']->accounts->get_account_name($this->account_id,$lid,$fname,$lname); - + // we cant use egw_info/user/fullname, as it's not set when we run $this->values = array( // standard notify replacements - 'fullname' => common::display_fullname('',$fname,$lname), - 'firstname' => $fname, - 'lastname' => $lname, + 'fullname' => Api\Accounts::id2name($this->account_id, 'account_fullname'), + 'firstname' => Api\Accounts::id2name($this->account_id, 'account_firstname'), + 'lastname' => Api\Accounts::id2name($this->account_id, 'account_lastname'), 'domain' => $GLOBALS['egw_info']['server']['mail_suffix'], 'email' => $this->email_address($this->account_id), 'date' => common::show_date('',$GLOBALS['egw_info']['user']['preferences']['common']['dateformat']),