From 71fc139a31d64901d737548e293177d74d7e5d4b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 27 Nov 2007 03:20:28 +0000 Subject: [PATCH] next step: all admin-cli commands are implemented now in the new structure, exist codes need to be reworked --- admin/admin-cli.php | 694 ++---------------- admin/inc/class.admin_cmd.inc.php | 117 ++- admin/inc/class.admin_cmd_account_app.inc.php | 20 +- .../class.admin_cmd_change_account_id.inc.php | 306 ++++++++ admin/inc/class.admin_cmd_change_pw.inc.php | 85 +++ admin/inc/class.admin_cmd_check_acl.inc.php | 65 ++ .../class.admin_cmd_delete_account.inc.php | 89 +++ admin/inc/class.admin_cmd_edit_group.inc.php | 124 ++++ admin/inc/class.admin_cmd_edit_user.inc.php | 202 +++++ admin/inc/class.admin_cmds.inc.php | 7 +- 10 files changed, 1047 insertions(+), 662 deletions(-) create mode 100644 admin/inc/class.admin_cmd_change_account_id.inc.php create mode 100644 admin/inc/class.admin_cmd_change_pw.inc.php create mode 100644 admin/inc/class.admin_cmd_check_acl.inc.php create mode 100644 admin/inc/class.admin_cmd_delete_account.inc.php create mode 100644 admin/inc/class.admin_cmd_edit_group.inc.php create mode 100644 admin/inc/class.admin_cmd_edit_user.inc.php diff --git a/admin/admin-cli.php b/admin/admin-cli.php index f4f9ef6c5d..ec0c0da611 100755 --- a/admin/admin-cli.php +++ b/admin/admin-cli.php @@ -63,7 +63,7 @@ switch($action) return do_edit_group($arg0s); case '--delete-group': - return do_delete_account($arg0s[2],0,'g'); + return do_delete_account($arg0s[2],0,false); case '--allow-app': case '--deny-app': @@ -89,19 +89,21 @@ exit(0); /** * run a command object, after checking for additional arguments: sheduled, requested or comment + * + * Does not return! Echos success or error messsage and exits with either 0 (success) or the numerical error-code * * @param admin_cmd $cmd - * @return string see admin_cmd::run() */ -function run_command($cmd) +function run_command(admin_cmd $cmd) { global $arguments; - + + $skip_checks = false; while ($arguments && ($extra = array_shift($arguments))) { switch($extra) { - case '--shedule': // shedule the command instead of running it directly + case '--schedule': // schedule the command instead of running it directly $time = admin_cmd::parse_date(array_shift($arguments)); break; @@ -118,6 +120,10 @@ function run_command($cmd) $cmd->remote_id = admin_cmd::parse_remote(array_shift($arguments)); break; + case '--skip-checks': //do not yet run the checks for scheduled local commands + $skip_checks = true; + break; + default: //fail(99,lang('Unknown option %1',$extra); echo lang('Unknown option %1',$extra)."\n\n"; @@ -126,7 +132,14 @@ function run_command($cmd) } } //_debug_array($cmd); - return $cmd->run($time); + $msg = $cmd->run($time,true,$skip_checks); + + if ($cmd->errno) + { + fail($cmd->errno,$cmd->error); + } + echo $msg."\n\n"; + exit(0); } /** @@ -167,7 +180,7 @@ function user_pass_from_argv(&$account) function usage($action=null,$ret=0) { $cmd = basename($_SERVER['argv'][0]); - echo "Usage: $cmd --command admin-account[@domain],admin-password,options,... [--schedule {YYYY-mm-dd|+1 week|+5 days}] [--requested 'Name '] [--comment 'comment ...'] [--remote {id|name}]\n\n"; + echo "Usage: $cmd --command admin-account[@domain],admin-password,options,... [--schedule {YYYY-mm-dd|+1 week|+5 days}] [--requested 'Name '] [--comment 'comment ...'] [--remote {id|name}] [--skip-checks]\n\n"; echo "--edit-user admin-account[@domain],admin-password,account[=new-account-name],first-name,last-name,password,email,expires{never(default)|YYYY-MM-DD|already},can-change-pw{yes(default)|no},anon-user{yes|no(default)},primary-group{Default(default)|...}[,groups,...]\n"; echo " Edit or add a user to eGroupWare. If you specify groups, they *replace* the exiting memberships!\n"; @@ -206,43 +219,7 @@ function do_account_app($args,$allow) $account = array_shift($args); include_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd_account_app.inc.php'); - $cmd = new admin_cmd_account_app($allow,$account,$args); - $msg = run_command($cmd); - if ($cmd->errno) - { - fail($cmd->errno,$cmd->error); - } - echo $msg."\n\n"; - return 0; - - if ($GLOBALS['egw']->acl->check('account_access',16,'admin')) // user is explicitly forbidden to edit accounts - { - fail(2,lang("Permission denied !!!")); - } - if (!($type = $GLOBALS['egw']->accounts->exists($account)) || !is_numeric($id=$account) && !($id = $GLOBALS['egw']->accounts->name2id($account))) - { - fail(15,lang("Unknown account: %1 !!!",$account)); - } - if ($type == 2 && $id > 0) $id = -$id; // groups use negative id's internally, fix it, if user given the wrong sign - - if (!($apps = _parse_apps($args))) - { - return false; - } - //echo "account=$account, type=$type, id=$id, apps: ".implode(', ',$apps)."\n"; - foreach($apps as $app) - { - if ($allow) - { - $GLOBALS['egw']->acl->add_repository($app,'run',$id,1); - } - else - { - $GLOBALS['egw']->acl->delete_repository($app,'run',$id); - } - } - echo lang('Applications run rights updated.')."\n\n"; - return 0; + run_command(new admin_cmd_account_app($allow,$account,$args)); } /** @@ -256,110 +233,39 @@ function do_edit_group($args) array_shift($args); // admin-pw list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name] - $account_exists = true; - if (!($data = $GLOBALS['egw']->accounts->read($account)) || $data['account_type'] != 'g') - { - $account_exists = false; - $data = array( - 'account_type' => 'g', - 'account_status' => 'A', // not used, but so we do the same thing as the web-interface - 'account_expires' => -1, - ); - } - if ($GLOBALS['egw']->acl->check('account_access',$account_exists?16:4,'admin')) // user is explicitly forbidden to edit or add groups - { - fail(2,lang("Permission denied !!!")); - } - if (!$account_exists && $new_account_name) - { - fail(12,lang("Unknown group to edit: %1 !!!",$account)); - } - if (($email = array_shift($args))) - { - $data['account_email'] = $email; - } - if (($data['account_members'] = _parse_users($args)) === false) - { - return false; - } - if (!$account_exists && !$account) - { - fail(13,lang("You have to specify an non-empty group-name!")); - } - if (!$account_exists || $new_account_name) $data['account_lid'] = $new_account_name ? $new_account_name : $account; - - if (!$account_exists && !$args) - { - fail(14,lang("A group needs at least one member!")); - } - if (!$GLOBALS['egw']->accounts->save($data)) - { - fail(11,lang("Error saving account!")); - } - $GLOBALS['hook_values'] = $data; - if (!$account_exists) $GLOBALS['hook_values']['old_name'] = $account; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => $account_exists ? 'editgroup' : 'addgroup' - ),False,True); // called for every app now, not only enabled ones) + $data = array( + 'account_lid' => $new_account_name, + 'account_email' => array_shift($args), + 'account_members' => $args, + ); + try { + admin_cmd::parse_account($account,false); - if ($data['account_members']) - { - $GLOBALS['egw']->accounts->set_members($data['account_members'],$data['account_id']); + foreach($data as $name => &$value) // existing account --> empty values mean dont change, not set them empty! + { + if ((string)$value === '') $value = null; + } } - echo lang("Account %1 %2",$account,$account_exists ? lang('updated') : lang("created with id #%1",$data['account_id']))."\n\n"; - return 0; + catch (Exception $e) { // new group + $data['account_lid'] = $account; + $account = false; + }; + run_command(new admin_cmd_edit_group($account,$data)); } /** * Change/Set Password for a given user - * 1: 2: 3: 4: + * 1: 2: 3: 4: * @param array $args admin-account[@domain],admin-password,account,password */ function do_change_pw($args) { array_shift($args); // admin-account array_shift($args); // admin-pw - $account = array_shift($args); // account - - $account_exists = true; - if (!($data = $GLOBALS['egw']->accounts->read($account)) || $data['account_type'] != 'u') - { - $account_exists = false; - $data = array('account_type' => 'u'); - } - if ($GLOBALS['egw']->acl->check('account_access',$account_exists?16:4,'admin')) // user is explicitly forbidden to edit or add users - { - fail(2,lang("Permission denied !!!")); - } - if (!$account_exists) - { - fail(5,lang("Unknown user to change pw: %1 !!!",$account)); - } - - foreach(array( - 'account_lid' => $account, - 'account_passwd' => !($arg=array_shift($args)) ? null : $arg, - ) as $name => $value) - { - if ($value === false) return false; // error in _parse_xyz() - //echo $name.': '.(is_array($value) ? implode(', ',$value) : $value)."\n"; - if (!is_null($value)) $data[$name] = $value; - } - if($account_exists && $data['account_passwd']) - { - $auth =& CreateObject('phpgwapi.auth'); - $auth->change_password(null, $data['account_passwd'], $data['account_id']); - $GLOBALS['hook_values']['account_id'] = $data['account_id']; - $GLOBALS['hook_values']['old_passwd'] = null; - $GLOBALS['hook_values']['new_passwd'] = $data['account_passwd']; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => 'changepassword' - ),False,True); // called for every app now, not only enabled ones) - } - - echo lang("Account %1 %2 ",$account,$account_exists). lang('updated')."\n\n"; - return 0; - + $account = array_shift($args); // account + $password = array_shift($args); // pw + + run_command(new admin_cmd_change_pw($account,$password)); } /** @@ -373,251 +279,44 @@ function do_edit_user($args) array_shift($args); // admin-pw list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name] - $account_exists = true; - if (!($data = $GLOBALS['egw']->accounts->read($account)) || $data['account_type'] != 'u') - { - $account_exists = false; - $data = array('account_type' => 'u'); - } - if ($GLOBALS['egw']->acl->check('account_access',$account_exists?16:4,'admin')) // user is explicitly forbidden to edit or add users - { - fail(2,lang("Permission denied !!!")); - } - if (!$account_exists && $new_account_name) - { - fail(5,lang("Unknown user to edit: %1 !!!",$account)); - } - //echo !$account_exists ? "add account $account:\n" : "edit account $account:\n"; - foreach(array( - 'account_lid' => $new_account_name ? $new_account_name : ($account_exists ? $data['account_lid'] : $account), - 'account_firstname' => !($arg=array_shift($args)) ? null : $arg, - 'account_lastname' => !($arg=array_shift($args)) ? null : $arg, - 'account_passwd' => !($arg=array_shift($args)) ? null : $arg, - 'account_email' => !($arg=array_shift($args)) ? null : $arg, - 'account_expires' => $expires=_parse_expired(!($expires=array_shift($args)) && !$account_exists ? 'never' : $expires), - 'account_status' => !$expires ? null : ($expires === -1 || $expires > time() ? 'A' : ''), - 'changepassword' => !($can_pw=array_shift($args)) && !$account_exists ? 'yes' : ($can_pw ? $can_pw : null), - 'anonymous' => !($is_anon=array_shift($args)) && !$account_exists ? 'no' : ($is_anon ? $is_anon : null), - 'account_primary_group' => _parse_groups(!($primary=array_shift($args)) && !$account_exists ? ($primary='Default') : $primary), - 'account_groups' => _parse_groups(!$args && !$account_exists ? array($primary ? $primary : 'Default') : $args), - ) as $name => $value) - { - if ($value === false) return false; // error in _parse_xyz() + $data = array( + 'account_lid' => $new_account_name, + 'account_firstname' => array_shift($args), + 'account_lastname' => array_shift($args), + 'account_passwd' => array_shift($args), + 'account_email' => array_shift($args), + 'account_expires' => array_shift($args), + 'changepassword' => array_shift($args), + 'anonymous' => array_shift($args), + 'account_primary_group' => array_shift($args), + 'account_groups' => $args, + ); + try { + admin_cmd::parse_account($account,true); - //echo $name.': '.(is_array($value) ? implode(', ',$value) : $value)."\n"; - if (!is_null($value)) $data[$name] = $value; - } - if (!$data['account_lid'] || !$account_exists && !$data['account_lastname']) - { - fail(9,lang("You have to specify an non-empty account-name and lastname!")); - } - if (!$account_exists && !$data['account_primary_group']) - { - fail(10,lang("You have to specify at least a primary group!")); - } - if ($data['groups'] && !in_array($data['account_primary_group'],$data['groups']) || !$account_exists && !$data['groups']) - { - $data['groups'][] = $data['account_primary_group']; - } - if (!$GLOBALS['egw']->accounts->save($data)) - { - fail(11,lang("Error saving account!")); - } - if ($data['account_groups']) - { - $GLOBALS['egw']->accounts->set_memberships($data['account_groups'],$data['account_id']); - } - if ($data['anonymous']) - { - if ($data['anonymous']{0} != 'n') + foreach($data as $name => &$value) // existing account --> empty values mean dont change, not set them empty! { - $GLOBALS['egw']->acl->add_repository('phpgwapi','anonymous',$data['account_id'],1); - } - else - { - $GLOBALS['egw']->acl->delete_repository('phpgwapi','anonymous',$data['account_id']); + if ((string)$value === '') $value = null; } } - if ($data['changepassword']) - { - if ($data['changepassword']{0} == 'n') - { - $GLOBALS['egw']->acl->add_repository('preferences','nopasswordchange',$data['account_id'],1); - } - else - { - $GLOBALS['egw']->acl->delete_repository('preferences','nopasswordchange',$data['account_id']); - } - } - if($account_exists && $data['account_passwd']) - { - $auth =& CreateObject('phpgwapi.auth'); - $auth->change_password(null, $data['account_passwd'], $data['account_id']); - $GLOBALS['hook_values']['account_id'] = $data['account_id']; - $GLOBALS['hook_values']['old_passwd'] = null; - $GLOBALS['hook_values']['new_passwd'] = $data['account_passwd']; - - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => 'changepassword' - ),False,True); // called for every app now, not only enabled ones) - } - $GLOBALS['hook_values'] = $data; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => $account_exists ? 'editaccount' : 'addaccount' - ),False,True); // called for every app now, not only enabled ones) - - echo lang("Account %1 %2",$account,$account_exists ? lang('updated') : lang("created with id #%1",$data['account_id']))."\n\n"; - return 0; -} - -/** - * parse application names, titles or localised names and return array of app-names - * - * @param array $apps names, titles or localised names - * @return array/boolean array of app-names or false if an app is not found - */ -function _parse_apps($apps) -{ - foreach($apps as $key => $name) - { - if (!isset($GLOBALS['egw_info']['apps'][$name])) - { - foreach($GLOBALS['egw_info']['apps'] as $app => $data) // check against title and localised name - { - if (!strcasecmp($name,$data['title']) || !strcasecmp($name,lang($app))) - { - $apps[$key] = $name = $app; - break; - } - } - } - if (!isset($GLOBALS['egw_info']['apps'][$name])) - { - fail(8,lang("Application '%1' not found (maybe not installed or misspelled)!",$name)); - return false; - } - } - return $apps; -} - -/** - * parse groups and return the group-id's - * - * @param string/array $groups group-id's or names - * @return string/array/boolean false on error - */ -function _parse_groups($groups) -{ - if (!$groups) return null; - - $ids = array(); - foreach(is_array($groups) ? $groups : array($groups) as $group) - { - if ($GLOBALS['egw']->accounts->exists($id = is_numeric($group) && $group > 0 ? -$group : $group) != 2 || - (!is_numeric($group) && !($id = $GLOBALS['egw']->accounts->name2id($group,'account_lid','g')))) - { - fail(8,lang("Unknown group: %1 !!!",$group)); - return false; - } - $ids[] = $id; - } - return is_string($groups) ? $ids[0] : $ids; -} - -/** - * parse users and return the user-id's - * - * @param string/array $users user-id's or names - * @return string/array/boolean false on error - */ -function _parse_users($users) -{ - if (!$users) return null; - - $ids = array(); - foreach(is_array($users) ? $users : array($users) as $user) - { - if ($GLOBALS['egw']->accounts->exists($id = $user) != 1 || - (!is_numeric($group) && !($id = $GLOBALS['egw']->accounts->name2id($user,'account_lid','u')))) - { - fail(7,lang("Unknown user: %1 !!!",$user)); - return false; - } - $ids[] = $id; - } - return is_string($groups) ? $ids[0] : $ids; -} - -/** - * parse the expired string and return the expired date as timestamp - * - * @param string $str - * @return int/boolean false on error - */ -function _parse_expired($str) -{ - switch($str) - { - case 'never': - return -1; - case 'already': - return 0; - case '': - return null; - } - // YYYY-MM-DD - list($y,$m,$d) = explode('-',$str); - if (!checkdate((int)$m,(int)$d,(int)$y)) - { - fail(6,lang("Invalid date '%1' use YYYY-MM-DD!",$str)); - return false; - } - return mktime(0,0,0,$m,$d,$y); + catch (Exception $e) { // new account + $data['account_lid'] = $account; + $account = false; + }; + run_command(new admin_cmd_edit_user($account,$data)); } /** * Delete a given acount from eGW * * @param int/string $account account-name of -id - * @param int/string $new_user=0 for uses only: account to move the entries too - * @param boolean $type='u' are we called for a user or group + * @param int/string $new_user=0 for users only: account to move the entries too + * @param boolean $is_user=true are we called for a user or group * @return int 0 on success, 2-4 otherwise (see source) */ -function do_delete_account($account,$new_user=0,$type='u') +function do_delete_account($account,$new_user=0,$is_user=true) { - //echo "do_delete_account('$account','$new_user',$do_group)\n"; - if ($GLOBALS['egw']->acl->check('account_access',32,'admin')) // user is explicitly forbidden to delete users - { - fail(2,lang("Permission denied !!!")); - } - if (!is_numeric($account) && !($id = $GLOBALS['egw']->accounts->name2id($lid=$account)) || - is_numeric($account) && !($lid = $GLOBALS['egw']->accounts->id2name($id=$account)) || - $GLOBALS['egw']->accounts->get_type($id) != $type) - { - fail(3,lang("Unknown account to delete: %1 !!!",$account)); - } - if ($new_user && (!is_numeric($new_user) && !($new_uid = $GLOBALS['egw']->accounts->name2id($new_user)) || - is_numeric($new_user) && !$GLOBALS['egw']->accounts->id2name($new_uid=$new_user))) - { - fail(4,lang("Unknown user to move to: %1 !!!",$new_user)); - } - // delete the account - $GLOBALS['hook_values'] = array( - 'account_id' => $id, - 'account_lid' => $lid, - 'account_name'=> $lid, // pericated name for deletegroup hook - 'new_owner' => (int)$new_uid, // deleteaccount only - 'location' => $type == 'u' ? 'deleteaccount' : 'deletegroup', - ); - // first all other apps, then preferences and admin - foreach(array_merge(array_diff(array_keys($GLOBALS['egw_info']['apps']),array('preferences','admin')),array('preferences','admin')) as $app) - { - $GLOBALS['egw']->hooks->single($GLOBALS['hook_values'],$app); - } - if ($type == 'g') $GLOBALS['egw']->accounts->delete($id); // groups get not deleted via the admin hook, as users - - echo lang("Account '%1' deleted.",$account)."\n\n"; - return 0; + run_command(new admin_cmd_delete_account($account,$new_user,$is_user)); } /** @@ -627,20 +326,7 @@ function do_delete_account($account,$new_user=0,$type='u') */ function do_check_acl() { - $deleted = 0; - if (($all_accounts = $GLOBALS['egw']->accounts->search(array('type'=>'both')))) - { - $ids = array(); - foreach($all_accounts as $account) - { - $ids[] = $account['account_id']; - } - // does not work for LDAP! $ids = array_keys($all_accounts); - $GLOBALS['egw']->db->query("DELETE FROM egw_acl WHERE acl_account NOT IN (".implode(',',$ids).") OR acl_appname='phpgw_group' AND acl_location NOT IN ('".implode("','",$ids)."')",__LINE__,__FILE__); - $deleted = $GLOBALS['egw']->db->affected_rows(); - } - echo lang("%1 ACL records of not (longer) existing accounts deleted.",$deleted)."\n\n"; - return 0; + run_command(new admin_cmd_check_acl()); } /** @@ -651,151 +337,6 @@ function do_check_acl() */ function do_change_account_id($args) { - /** - * App-, Table- and column-names containing nummeric account-id's - * @var array - */ - $columns2change = array( - 'phpgwapi' => array( - 'egw_access_log' => 'account_id', - 'egw_accounts' => array(array('account_id','.type'=>'abs'),'account_primary_group'), - 'egw_acl' => array('acl_account','acl_location'), - 'egw_addressbook' => array('contact_owner','contact_creator','contact_modifier','account_id'), - 'egw_addressbook2list' => array('list_added_by'), - 'egw_addressbook_extra' => 'contact_owner', - 'egw_addressbook_lists' => array('list_owner','list_creator'), - 'egw_api_content_history' => 'sync_changedby', - 'egw_applications' => false, - 'egw_app_sessions' => 'loginid', - 'egw_async' => 'async_account_id', - 'egw_categories' => array(array('cat_owner','cat_owner > 0')), // -1 are global cats, not cats from group 1! - 'egw_config' => false, - 'egw_history_log' => 'history_owner', - 'egw_hooks' => false, - 'egw_interserv' => false, - 'egw_lang' => false, - 'egw_languages' => false, - 'egw_links' => 'link_owner', - 'egw_log' => 'log_user', - 'egw_log_msg' => false, - 'egw_nextid' => false, - 'egw_preferences' => array(array('preference_owner','preference_owner > 0')), - 'egw_sessions' => false, // only account_lid stored - 'egw_vfs' => array('vfs_owner_id','vfs_createdby_id','vfs_modifiedby_id'), // 'vfs_directory' contains account_lid for /home/account - ), - 'etemplate' => array( - 'egw_etemplate' => 'et_group', - ), - 'bookmarks' => array( - 'egw_bookmarks' => 'bm_owner', - ), - 'calendar' => array( - 'egw_cal' => array('cal_owner','cal_modifier'), - 'egw_cal_dates' => false, - 'egw_cal_extra' => false, - 'egw_cal_holidays' => false, - 'egw_cal_repeats' => false, - 'egw_cal_user' => array(array('cal_user_id','cal_user_type' => 'u')), // cal_user_id for cal_user_type='u' - ), - 'emailadmin' => array( - 'egw_emailadmin' => false, - ), - 'felamimail' => array( - 'egw_felamimail_accounts' => 'fm_owner', - 'egw_felamimail_cache' => 'fmail_accountid', // afaik not used in 1.4+ - 'egw_felamimail_displayfilter' => 'fmail_filter_accountid', - 'egw_felamimail_folderstatus' => 'fmail_accountid', // afaik not used in 1.4+ - 'egw_felamimail_signatures' => 'fm_accountid', - ), - 'infolog' => array( - 'egw_infolog' => array('info_owner',array('info_responsible','.type' => 'comma-sep'),'info_modifier'), - 'egw_infolog_extra' => false, - ), - 'news_admin' => array( - 'egw_news' => 'news_submittedby', - 'egw_news_export' => false, - ), - 'projectmanager' => array( - 'egw_pm_constraints' => false, - 'egw_pm_elements' => array('pe_modifier',array('pe_resources','.type' => 'comma-sep')), - 'egw_pm_extra' => false, - 'egw_pm_members' => 'member_uid', - 'egw_pm_milestones' => false, - 'egw_pm_pricelist' => false, - 'egw_pm_prices' => 'pl_modifier', - 'egw_pm_projects' => array('pm_creator','pm_modifier'), - 'egw_pm_roles' => false, - ), - 'registration' => array( - 'egw_reg_accounts' => false, - 'egw_reg_fields' => false, - ), - 'resources' => array( - 'egw_resources' => false, - 'egw_resources_extra'=> 'extra_owner', - ), - 'sitemgr' => array( - 'egw_sitemgr_active_modules' => false, - 'egw_sitemgr_blocks' => false, - 'egw_sitemgr_blocks_lang' => false, - 'egw_sitemgr_categories_lang' => false, - 'egw_sitemgr_categories_state' => false, - 'egw_sitemgr_content' => false, - 'egw_sitemgr_content_lang' => false, - 'egw_sitemgr_modules' => false, - 'egw_sitemgr_notifications' => false, - 'egw_sitemgr_notify_messages' => false, - 'egw_sitemgr_pages' => false, - 'egw_sitemgr_pages_lang' => false, - 'egw_sitemgr_properties' => false, - 'egw_sitemgr_sites' => false, - ), - 'syncml' => array( - 'egw_contentmap' => false, - 'egw_syncmldeviceowner' => false, // Lars: is owner_devid a account_id??? - 'egw_syncmldevinfo' => false, - 'egw_syncmlsummary' => false, - ), - 'tracker' => array( - 'egw_tracker' => array('tr_assigned','tr_creator','tr_modifier'), - 'egw_tracker_bounties' => array('bounty_creator','bounty_confirmer'), - 'egw_tracker_replies' => array('reply_creator'), - 'egw_tracker_votes' => array('vote_uid'), - ), - 'timesheet' => array( - 'egw_timesheet' => array('ts_owner','ts_modifier'), - 'egw_timesheet_extra'=> false, - ), - 'wiki' => array( - 'egw_wiki_interwiki' => false, - 'egw_wiki_links' => false, - 'egw_wiki_pages' => array(array('wiki_readable','wiki_readable < 0'),array('wiki_writable','wiki_writable < 0')), // only groups - 'egw_wiki_rate' => false, - 'egw_wiki_remote_pages' => false, - 'egw_wiki_sisterwiki'=> false, - ), - 'phpbrain' => array( // aka knowledgebase - 'phpgw_kb_articles' => array('user_id','modified_user_id'), - 'phpgw_kb_comment' => 'user_id', - 'phpgw_kb_files' => false, - 'phpgw_kb_questions' => 'user_id', - 'phpgw_kb_ratings' => 'user_id', - 'phpgw_kb_related_art' => false, - 'phpgw_kb_search' => false, - 'phpgw_kb_urls' => false, - ), - 'polls' => array( - 'egw_polls' => false, - 'egw_polls_answers' => false, - 'egw_polls_votes' => 'vote_uid', - ), - 'gallery' => array( - 'g2_ExternalIdMap' => array(array('g_externalId',"g_entityType='GalleryUser'")), - ), - // MyDMS ToDo!!! - // VFS2 ToDo!!! - ); - if (count($args) < 4) usage(); // 4 means at least user,pw,from1,to1 $ids2change = array(); @@ -803,106 +344,9 @@ function do_change_account_id($args) { $from = (int)$args[$n]; $to = (int)$args[$n+1]; - if (!$from || !$to) - { - fail(16,lang("Account-id's have to be integers!")); - } $ids2change[$from] = $to; } - $total = 0; - foreach($columns2change as $app => $data) - { - $db = clone($GLOBALS['egw']->db); - $db->set_app($app); - - foreach($data as $table => $columns) - { - if (!$columns) - { - echo "$app: $table no columns with account-id's\n"; - continue; // noting to do for this table - } - if (!is_array($columns)) $columns = array($columns); - - foreach($columns as $column) - { - $type = $where = null; - if (is_array($column)) - { - $type = $column['.type']; - unset($column['.type']); - $where = $column; - $column = array_shift($where); - } - $total += ($changed = _update_account_id($ids2change,$db,$table,$column,$where,$type)); - echo "$app: $table.$column $changed id's changed\n"; - } - } - } - echo lang("Total of %1 id's changed.",$total)."\n\n"; - return 0; -} - -function _update_account_id($ids2change,$db,$table,$column,$where=null,$type=null) -{ - static $update_sql; - - if (is_null($update_sql)) - { - foreach($ids2change as $from => $to) - { - $update_sql .= "WHEN $from THEN $to "; - } - $update_sql .= "END"; - } - switch($type) - { - case 'comma-sep': - if (!$where) $where = array(); - $where[] = "$column IS NOT NULL"; - $where[] = "$column != ''"; - $db->select($table,'DISTINCT '.$column,$where,__LINE__,__FILE__); - $change = array(); - while(($row = $db->row(true))) - { - $ids = explode(',',$old_ids=$row[$column]); - foreach($ids as $key => $id) - { - if (isset($account_id2change[$id])) $ids[$key] = $account_id2change[$id]; - } - $ids = implode(',',$ids); - if ($ids != $old_ids) - { - $change[$old_ids] = $ids; - } - } - $changed = 0; - foreach($change as $from => $to) - { - $db->update($table,array($column=>$to),$where+array($column=>$from),__LINE__,__FILE__); - $changed += $db->affected_rows(); - } - break; - - case 'abs': - if (!$where) $where = array(); - $where[$column] = array(); - foreach(array_keys($ids2change) as $id) - { - $where[$column][] = abs($id); - } - $db->update($table,$column.'= CASE '.$column.' '.preg_replace('/-([0-9]+)/','\1',$update_sql),$where,__LINE__,__FILE__); - $changed = $db->affected_rows(); - break; - - default: - if (!$where) $where = array(); - $where[$column] = array_keys($ids2change); - $db->update($table,$column.'= CASE '.$column.' '.$update_sql,$where,__LINE__,__FILE__); - $changed = $db->affected_rows(); - break; - } - return $changed; + run_command(new admin_cmd_change_account_id($ids2change)); } /** diff --git a/admin/inc/class.admin_cmd.inc.php b/admin/inc/class.admin_cmd.inc.php index 315592e999..293f1bd44a 100644 --- a/admin/inc/class.admin_cmd.inc.php +++ b/admin/inc/class.admin_cmd.inc.php @@ -90,10 +90,11 @@ abstract class admin_cmd /** * Executes the command * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself * @return string success message * @throws Exception() */ - abstract function exec(); + protected abstract function exec($check_only=false); /** * Return a title / string representation for a given command, eg. to display it @@ -130,17 +131,31 @@ abstract class admin_cmd * * The command will be written to the database queue, incl. its scheduled start time or execution status * - * @param $time=null timestamp to run the command or null to run it immediatly - * @param $set_modifier=null should the current user be set as modifier, default true + * @param int $time=null timestamp to run the command or null to run it immediatly + * @param boolean $set_modifier=null should the current user be set as modifier, default true + * @param booelan $skip_checks=false do not yet run the checks for a scheduled command * @return mixed string with execution error or success message, false for other errors */ - function run($time=null,$set_modifier=true) + function run($time=null,$set_modifier=true,$skip_checks=false) { if (!is_null($time)) { $this->scheduled = $time; $this->status = admin_cmd::scheduled; $ret = lang('Command scheduled to run at %1',date('Y-m-d H:i',$time)); + // running the checks of the arguments for local commands, if not explicitly requested to not run them + if (!$this->remote_id && !$skip_checks) + { + try { + $this->exec(true); + } + catch (Exception $e) { + $this->error = $e->getMessage(); + $ret = $this->errno = $e->getCode(); + $this->status = admin_cmd::failed; + $dont_save = true; + } + } } else { @@ -161,7 +176,7 @@ abstract class admin_cmd $this->status = admin_cmd::failed; } } - if (!$this->save($set_modifier)) + if (!$dont_save && !$this->save($set_modifier)) { return false; } @@ -316,12 +331,7 @@ abstract class admin_cmd } if (!class_exists($class = $data['type'])) { - list($app) = explode('_',$class); - @include_once(EGW_INCLUDE_ROOT.'/'.$app.'/inc/class.'.$class.'.inc.php'); - if (!class_exists($class)) - { - throw new Exception(lang('Unknown command %1!',$class),0); - } + throw new Exception(lang('Unknown command %1!',$class),0); } $cmd = new $class($data); @@ -474,7 +484,7 @@ abstract class admin_cmd * @return array of app-names * @throws Exception(lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8); */ - protected static function _parse_apps(array $apps) + static function parse_apps(array $apps) { foreach($apps as $key => $name) { @@ -500,34 +510,57 @@ abstract class admin_cmd /** * parse account name or id * - * @param string/int $account - * @param boolean $allow_only=null true=only user, false=only groups, default both - * @return int account_id - * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); - * @throws Exception(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only?lang('user'):lang('group')),15); + * @param string/int $account account_id or account_lid + * @param boolean $allow_only_user=null true=only user, false=only groups, default both + * @return int/array account_id + * @throws Exception(lang("Unknown account: %1 !!!",$account),15); + * @throws Exception(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only_user?lang('user'):lang('group')),15); */ - protected static function _parse_account($account,$allow_only=null) + static function parse_account($account,$allow_only_user=null) { + admin_cmd::_instanciate_accounts(); + if (!($type = admin_cmd::$accounts->exists($account)) || !is_numeric($id=$account) && !($id = admin_cmd::$accounts->name2id($account))) { throw new Exception(lang("Unknown account: %1 !!!",$account),15); } - if (!is_null($allow_only) && $allow_only !== ($type == 1)) + if (!is_null($allow_only_user) && $allow_only_user !== ($type == 1)) { - throw new Exception(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only?lang('user'):lang('group')),15); + throw new Exception(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only_user?lang('user'):lang('group')),15); } if ($type == 2 && $id > 0) $id = -$id; // groups use negative id's internally, fix it, if user given the wrong sign return $id; } + /** + * parse account names or ids + * + * @param string/int/array $accounts array or comma-separated account_id's or account_lid's + * @param boolean $allow_only_user=null true=only user, false=only groups, default both + * @return array of account_id's or null if none specified + * @throws Exception(lang("Unknown account: %1 !!!",$account),15); + * @throws Exception(lang("Wrong account type: %1 is NO %2 !!!",$account,$allow_only?lang('user'):lang('group')),15); + */ + static function parse_accounts($accounts,$allow_only_user=null) + { + if (!$accounts) return null; + + $ids = array(); + foreach(is_array($accounts) ? $accounts : explode(',',$accounts) as $account) + { + $ids[] = admin_cmd::parse_account($account,$allow_only_user); + } + return $ids; + } + /** * Parses a date into an integer timestamp * * @param string $date * @return int timestamp - * @throws Exception(lang('Invalid formated date "%1"!',$datein),998); + * @throws Exception(lang('Invalid formated date "%1"!',$datein),6); */ static function parse_date($date) { @@ -539,12 +572,37 @@ abstract class admin_cmd if (($date = strtotime($date)) === false) { - throw new Exception(lang('Invalid formated date "%1"!',$datein),998); + throw new Exception(lang('Invalid formated date "%1"!',$datein),6); } } return (int)$date; } + /** + * Parse a boolean value + * + * @param string $value + * @param boolean $default=null + * @return boolean + * @throws Exception(lang('Invalid value "%1" use yes or no!',$value),998); + */ + static function parse_boolean($value,$default=null) + { + if (is_null($value) || (string)$value === '') + { + return $default; + } + if (in_array($value,array('1','yes','true',lang('yes'),lang('true')))) + { + return true; + } + if (in_array($value,array('0','no','false',lang('no'),lang('false')))) + { + return false; + } + throw new Exception(lang('Invalid value "%1" use yes or no!',$value),998); + } + /** * Parse a remote id or name and return the remote_id * @@ -777,4 +835,19 @@ abstract class admin_cmd return admin_cmd::$remote->save() == 0 ? admin_cmd::$remote->data : false; } + + /** + * displays an account specified by it's id or lid + * + * We show the value given by the user, plus the full name in brackets. + * + * @param int/string $account + * @return string + */ + static function display_account($account) + { + $id = is_numeric($account) ? $account : $GLOBALS['egw']->accounts->id2name($account); + + return $account.' ('.$GLOBALS['egw']->common->grab_owner_name($id).')'; + } } diff --git a/admin/inc/class.admin_cmd_account_app.inc.php b/admin/inc/class.admin_cmd_account_app.inc.php index 2e71f84801..077fbed12a 100644 --- a/admin/inc/class.admin_cmd_account_app.inc.php +++ b/admin/inc/class.admin_cmd_account_app.inc.php @@ -10,8 +10,6 @@ * @version $Id$ */ -require_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd.inc.php'); - /** * admin command: give or remove run rights from a given account and application */ @@ -38,28 +36,30 @@ class admin_cmd_account_app extends admin_cmd { $allow['apps'] = explode(',',$allow['apps']); } - parent::__construct($allow); + admin_cmd::__construct($allow); } /** * give or remove run rights from a given account and application * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself * @return string success message * @throws Exception(lang("Permission denied !!!"),2) * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); * @throws Exception(lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8); */ - function exec() + protected function exec($check_only=false) { - admin_cmd::_instanciate_acl($this->creator); - admin_cmd::_instanciate_accounts(); - - $account_id = admin_cmd::_parse_account($this->account); + $account_id = admin_cmd::parse_account($this->account); // check creator is still admin and not explicitly forbidden to edit accounts/groups if ($this->creator) $this->_check_admin($account_id > 0 ? 'account_access' : 'group_access',16); - $apps = admin_cmd::_parse_apps($this->apps); + $apps = admin_cmd::parse_apps($this->apps); + + if ($check_only) return true; + //echo "account=$this->account, account_id=$account_id, apps: ".implode(', ',$apps)."\n"; + admin_cmd::_instanciate_acl($account_id); foreach($apps as $app) { if ($this->allow) @@ -82,6 +82,6 @@ class admin_cmd_account_app extends admin_cmd function __tostring() { return lang('%1 rights for %2 and applications %3',$this->allow ? lang('Grant') : lang('Remove'), - $this->account,implode(', ',$this->apps)); + admin_cmd::display_account($this->account),implode(', ',$this->apps)); } } diff --git a/admin/inc/class.admin_cmd_change_account_id.inc.php b/admin/inc/class.admin_cmd_change_account_id.inc.php new file mode 100644 index 0000000000..baec777057 --- /dev/null +++ b/admin/inc/class.admin_cmd_change_account_id.inc.php @@ -0,0 +1,306 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: change an account_id + */ +class admin_cmd_change_account_id extends admin_cmd +{ + /** + * Constructor + * + * @param array $change array with old => new id pairs + */ + function __construct(array $change) + { + if (!is_set($change['change'])) + { + $change = array( + 'change' => $change, + ); + } + admin_cmd::__construct($change); + } + + /** + * App-, Table- and column-names containing nummeric account-id's + * @var array + */ + private $columns2change = array( + 'phpgwapi' => array( + 'egw_access_log' => 'account_id', + 'egw_accounts' => array(array('account_id','.type'=>'abs'),'account_primary_group'), + 'egw_acl' => array('acl_account','acl_location'), + 'egw_addressbook' => array('contact_owner','contact_creator','contact_modifier','account_id'), + 'egw_addressbook2list' => array('list_added_by'), + 'egw_addressbook_extra' => 'contact_owner', + 'egw_addressbook_lists' => array('list_owner','list_creator'), + 'egw_api_content_history' => 'sync_changedby', + 'egw_applications' => false, + 'egw_app_sessions' => 'loginid', + 'egw_async' => 'async_account_id', + 'egw_categories' => array(array('cat_owner','cat_owner > 0')), // -1 are global cats, not cats from group 1! + 'egw_config' => false, + 'egw_history_log' => 'history_owner', + 'egw_hooks' => false, + 'egw_interserv' => false, + 'egw_lang' => false, + 'egw_languages' => false, + 'egw_links' => 'link_owner', + 'egw_log' => 'log_user', + 'egw_log_msg' => false, + 'egw_nextid' => false, + 'egw_preferences' => array(array('preference_owner','preference_owner > 0')), + 'egw_sessions' => false, // only account_lid stored + 'egw_vfs' => array('vfs_owner_id','vfs_createdby_id','vfs_modifiedby_id'), // 'vfs_directory' contains account_lid for /home/account + ), + 'etemplate' => array( + 'egw_etemplate' => 'et_group', + ), + 'bookmarks' => array( + 'egw_bookmarks' => 'bm_owner', + ), + 'calendar' => array( + 'egw_cal' => array('cal_owner','cal_modifier'), + 'egw_cal_dates' => false, + 'egw_cal_extra' => false, + 'egw_cal_holidays' => false, + 'egw_cal_repeats' => false, + 'egw_cal_user' => array(array('cal_user_id','cal_user_type' => 'u')), // cal_user_id for cal_user_type='u' + ), + 'emailadmin' => array( + 'egw_emailadmin' => false, + ), + 'felamimail' => array( + 'egw_felamimail_accounts' => 'fm_owner', + 'egw_felamimail_cache' => 'fmail_accountid', // afaik not used in 1.4+ + 'egw_felamimail_displayfilter' => 'fmail_filter_accountid', + 'egw_felamimail_folderstatus' => 'fmail_accountid', // afaik not used in 1.4+ + 'egw_felamimail_signatures' => 'fm_accountid', + ), + 'infolog' => array( + 'egw_infolog' => array('info_owner',array('info_responsible','.type' => 'comma-sep'),'info_modifier'), + 'egw_infolog_extra' => false, + ), + 'news_admin' => array( + 'egw_news' => 'news_submittedby', + 'egw_news_export' => false, + ), + 'projectmanager' => array( + 'egw_pm_constraints' => false, + 'egw_pm_elements' => array('pe_modifier',array('pe_resources','.type' => 'comma-sep')), + 'egw_pm_extra' => false, + 'egw_pm_members' => 'member_uid', + 'egw_pm_milestones' => false, + 'egw_pm_pricelist' => false, + 'egw_pm_prices' => 'pl_modifier', + 'egw_pm_projects' => array('pm_creator','pm_modifier'), + 'egw_pm_roles' => false, + ), + 'registration' => array( + 'egw_reg_accounts' => false, + 'egw_reg_fields' => false, + ), + 'resources' => array( + 'egw_resources' => false, + 'egw_resources_extra'=> 'extra_owner', + ), + 'sitemgr' => array( + 'egw_sitemgr_active_modules' => false, + 'egw_sitemgr_blocks' => false, + 'egw_sitemgr_blocks_lang' => false, + 'egw_sitemgr_categories_lang' => false, + 'egw_sitemgr_categories_state' => false, + 'egw_sitemgr_content' => false, + 'egw_sitemgr_content_lang' => false, + 'egw_sitemgr_modules' => false, + 'egw_sitemgr_notifications' => false, + 'egw_sitemgr_notify_messages' => false, + 'egw_sitemgr_pages' => false, + 'egw_sitemgr_pages_lang' => false, + 'egw_sitemgr_properties' => false, + 'egw_sitemgr_sites' => false, + ), + 'syncml' => array( + 'egw_contentmap' => false, + 'egw_syncmldeviceowner' => false, // Lars: is owner_devid a account_id??? + 'egw_syncmldevinfo' => false, + 'egw_syncmlsummary' => false, + ), + 'tracker' => array( + 'egw_tracker' => array('tr_assigned','tr_creator','tr_modifier'), + 'egw_tracker_bounties' => array('bounty_creator','bounty_confirmer'), + 'egw_tracker_replies' => array('reply_creator'), + 'egw_tracker_votes' => array('vote_uid'), + ), + 'timesheet' => array( + 'egw_timesheet' => array('ts_owner','ts_modifier'), + 'egw_timesheet_extra'=> false, + ), + 'wiki' => array( + 'egw_wiki_interwiki' => false, + 'egw_wiki_links' => false, + 'egw_wiki_pages' => array(array('wiki_readable','wiki_readable < 0'),array('wiki_writable','wiki_writable < 0')), // only groups + 'egw_wiki_rate' => false, + 'egw_wiki_remote_pages' => false, + 'egw_wiki_sisterwiki'=> false, + ), + 'phpbrain' => array( // aka knowledgebase + 'phpgw_kb_articles' => array('user_id','modified_user_id'), + 'phpgw_kb_comment' => 'user_id', + 'phpgw_kb_files' => false, + 'phpgw_kb_questions' => 'user_id', + 'phpgw_kb_ratings' => 'user_id', + 'phpgw_kb_related_art' => false, + 'phpgw_kb_search' => false, + 'phpgw_kb_urls' => false, + ), + 'polls' => array( + 'egw_polls' => false, + 'egw_polls_answers' => false, + 'egw_polls_votes' => 'vote_uid', + ), + 'gallery' => array( + 'g2_ExternalIdMap' => array(array('g_externalId',"g_entityType='GalleryUser'")), + ), + // MyDMS ToDo!!! + // VFS2 ToDo!!! + ); + + /** + * give or remove run rights from a given account and application + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + * @throws Exception(lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8); + */ + protected function exec($check_only=false) + { + foreach($this->change as $from => $to) + { + if (!(int)$from || !(int)$to) + { + throw new Exception (lang("Account-id's have to be integers!"),16); + } + } + if ($check_only) return true; + + $total = 0; + foreach($this->columns2change as $app => $data) + { + $db = clone($GLOBALS['egw']->db); + $db->set_app($app); + + foreach($data as $table => $columns) + { + if (!$columns) + { + echo "$app: $table no columns with account-id's\n"; + continue; // noting to do for this table + } + if (!is_array($columns)) $columns = array($columns); + + foreach($columns as $column) + { + $type = $where = null; + if (is_array($column)) + { + $type = $column['.type']; + unset($column['.type']); + $where = $column; + $column = array_shift($where); + } + $total += ($changed = self::_update_account_id($ids2change,$db,$table,$column,$where,$type)); + echo "$app: $table.$column $changed id's changed\n"; + } + } + } + return lang("Total of %1 id's changed.",$total)."\n\n"; + } + + private static function _update_account_id($ids2change,$db,$table,$column,$where=null,$type=null) + { + static $update_sql; + + if (is_null($update_sql)) + { + foreach($ids2change as $from => $to) + { + $update_sql .= "WHEN $from THEN $to "; + } + $update_sql .= "END"; + } + switch($type) + { + case 'comma-sep': + if (!$where) $where = array(); + $where[] = "$column IS NOT NULL"; + $where[] = "$column != ''"; + $db->select($table,'DISTINCT '.$column,$where,__LINE__,__FILE__); + $change = array(); + while(($row = $db->row(true))) + { + $ids = explode(',',$old_ids=$row[$column]); + foreach($ids as $key => $id) + { + if (isset($account_id2change[$id])) $ids[$key] = $account_id2change[$id]; + } + $ids = implode(',',$ids); + if ($ids != $old_ids) + { + $change[$old_ids] = $ids; + } + } + $changed = 0; + foreach($change as $from => $to) + { + $db->update($table,array($column=>$to),$where+array($column=>$from),__LINE__,__FILE__); + $changed += $db->affected_rows(); + } + break; + + case 'abs': + if (!$where) $where = array(); + $where[$column] = array(); + foreach(array_keys($ids2change) as $id) + { + $where[$column][] = abs($id); + } + $db->update($table,$column.'= CASE '.$column.' '.preg_replace('/-([0-9]+)/','\1',$update_sql),$where,__LINE__,__FILE__); + $changed = $db->affected_rows(); + break; + + default: + if (!$where) $where = array(); + $where[$column] = array_keys($ids2change); + $db->update($table,$column.'= CASE '.$column.' '.$update_sql,$where,__LINE__,__FILE__); + $changed = $db->affected_rows(); + break; + } + return $changed; + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + $change = array(); + foreach($this->change as $from => $to) $change[] = $from.'->'.$to; + + return lang('Change account_id').': '.implode(', ',$change); + } +} diff --git a/admin/inc/class.admin_cmd_change_pw.inc.php b/admin/inc/class.admin_cmd_change_pw.inc.php new file mode 100644 index 0000000000..4901454365 --- /dev/null +++ b/admin/inc/class.admin_cmd_change_pw.inc.php @@ -0,0 +1,85 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: change the password of a given user + */ +class admin_cmd_change_pw extends admin_cmd +{ + /** + * Constructor + * + * @param string/int/array $account account name or id, or array with all parameters + * @param string $password=null password + */ + function __construct($account,$password=null) + { + if (!is_array($account)) + { + $account = array( + 'account' => $account, + 'password' => $password, + ); + } + admin_cmd::__construct($account); + } + + /** + * change the password of a given user + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + * @throws Exception(lang('Error changing the password for %1 !!!',$this->account),99); + */ + protected function exec($check_only=false) + { + $account_id = admin_cmd::parse_account($this->account,true); // true = user, no group + // check creator is still admin and not explicitly forbidden to edit accounts + if ($this->creator) $this->_check_admin('account_access',16); + + if ($check_only) return true; + + $auth =& new auth; + + if (!$auth->change_password(null, $this->password, $account_id)) + { + // as long as the auth class is not throwing itself ... + throw new Exception(lang('Error changing the password for % !!!',$this->account),99); + } + $GLOBALS['hook_values']['account_id'] = $account_id; + $GLOBALS['hook_values']['account_lid'] = $this->account; + if (is_numeric($this->account)) + { + admin_cmd::_instanciate_accounts(); + $GLOBALS['hook_values']['account_lid'] = admin_cmd::$accounts->id2name($this->account); + } + $GLOBALS['hook_values']['old_passwd'] = null; + $GLOBALS['hook_values']['new_passwd'] = $this->password; + $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( + 'location' => 'changepassword' + ),False,True); // called for every app now, not only enabled ones) + + return lang('Password updated'); + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + return lang('change password for %1',admin_cmd::display_account($this->account)); + } +} diff --git a/admin/inc/class.admin_cmd_check_acl.inc.php b/admin/inc/class.admin_cmd_check_acl.inc.php new file mode 100644 index 0000000000..95b2336c0c --- /dev/null +++ b/admin/inc/class.admin_cmd_check_acl.inc.php @@ -0,0 +1,65 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: check ACL for entries of deleted accounts + */ +class admin_cmd_check_acl extends admin_cmd +{ + /** + * Constructor + * + * @param array $data=array() default parm from parent class, no real parameters + */ + function __construct($data=array()) + { + admin_cmd::__construct($data); + } + + /** + * give or remove run rights from a given account and application + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + * @throws Exception(lang("Application '%1' not found (maybe not installed or misspelled)!",$name),8); + */ + protected function exec($check_only=false) + { + if ($check_only) return true; + + admin_cmd::_instanciate_accounts(); + $deleted = 0; + if (($all_accounts = admin_cmd::$accounts->search(array('type'=>'both')))) + { + $ids = array(); + foreach($all_accounts as $account) + { + $ids[] = $account['account_id']; + } + $GLOBALS['egw']->db->query("DELETE FROM egw_acl WHERE acl_account NOT IN (".implode(',',$ids).") OR acl_appname='phpgw_group' AND acl_location NOT IN ('".implode("','",$ids)."')",__LINE__,__FILE__); + $deleted = $GLOBALS['egw']->db->affected_rows(); + } + return lang("%1 ACL records of not (longer) existing accounts deleted.",$deleted); + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + return lang('Check ACL for entries of not (longer) existing accounts'); + } +} diff --git a/admin/inc/class.admin_cmd_delete_account.inc.php b/admin/inc/class.admin_cmd_delete_account.inc.php new file mode 100644 index 0000000000..ac2ec389aa --- /dev/null +++ b/admin/inc/class.admin_cmd_delete_account.inc.php @@ -0,0 +1,89 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: delete an account (user or group) + */ +class admin_cmd_delete_account extends admin_cmd +{ + /** + * Constructor + * + * @param string/int/array $account account name or id, or array with all parameters + * @param string $new_user=null if specified, account to transfer the data to (users only) + * @param string $is_user=true type of the account: true=user, false=group + */ + function __construct($account,$new_user=null,$is_user=true) + { + if (!is_array($account)) + { + $account = array( + 'account' => $account, + 'new_user' => $new_user, + 'is_user' => $is_user, + ); + } + admin_cmd::__construct($account); + } + + /** + * delete an account (user or group) + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + * @throws Exception(lang('Error changing the password for %1 !!!',$this->account),99); + */ + protected function exec($check_only=false) + { + $account_id = admin_cmd::parse_account($this->account,$this->is_user); + admin_cmd::_instanciate_accounts(); + $account_lid = admin_cmd::$accounts->id2name($account_id); + + if ($this->is_user && $this->new_user) + { + $new_user = admin_cmd::parse_account($this->new_user,true); // true = user, no group + } + // check creator is still admin and not explicitly forbidden to edit accounts + if ($this->creator) $this->_check_admin($this->is_user ? 'account_access' : 'group_access',32); + + if ($check_only) return true; + + // delete the account + $GLOBALS['hook_values'] = array( + 'account_id' => $account_id, + 'account_lid' => $account_lid, + 'account_name'=> $account_lid, // depericated name for deletegroup hook + 'new_owner' => (int)$new_user, // deleteaccount only + 'location' => $this->is_user ? 'deleteaccount' : 'deletegroup', + ); + // first all other apps, then preferences and admin + foreach(array_merge(array_diff(array_keys($GLOBALS['egw_info']['apps']),array('preferences','admin')),array('preferences','admin')) as $app) + { + $GLOBALS['egw']->hooks->single($GLOBALS['hook_values'],$app); + } + if (!$this->is_user) $GLOBALS['egw']->accounts->delete($account_id); // groups get not deleted via the admin hook, as users + + return lang("Account '%1' deleted.",$this->account)."\n\n"; + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + return lang('Delete account %1',admin_cmd::display_account($this->account)); + } +} diff --git a/admin/inc/class.admin_cmd_edit_group.inc.php b/admin/inc/class.admin_cmd_edit_group.inc.php new file mode 100644 index 0000000000..ab74e0a0b2 --- /dev/null +++ b/admin/inc/class.admin_cmd_edit_group.inc.php @@ -0,0 +1,124 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: edit/add a user + */ +class admin_cmd_edit_group extends admin_cmd +{ + /** + * Constructor + * + * @param string/int/array $account account name or id (!$account to add a new account), or array with all parameters + * @param array $set=null array with all data to change + */ + function __construct($account,$set=null) + { + if (!is_array($account)) + { + $account = array( + 'account' => $account, + 'set' => $set, + ); + } + admin_cmd::__construct($account); + } + + /** + * change the password of a given user + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + */ + protected function exec($check_only=false) + { + // check creator is still admin and not explicitly forbidden to edit accounts/groups + if ($this->creator) $this->_check_admin('group_access',$this->account ? 16 : 4); + + admin_cmd::_instanciate_accounts(); + + $data = $this->set; + + if ($this->account) // existing account + { + $data['account_id'] = admin_cmd::parse_account($this->account,false); + } + else + { + $data += array( + 'account_type' => 'g', + 'account_status' => 'A', // not used, but so we do the same thing as the web-interface + 'account_expires' => -1, + ); + } + if (!$data['account_lid'] && (!$this->account || !is_null($data['account_lid']))) + { + throw new Exception(lang('You must enter a group name.'),9); + } + if (!is_null($data['account_lid']) && ($id = admin_cmd::$accounts->name2id($data['account_lid'],'account_lid','g')) && + $id !== $data['account_id']) + { + throw new Exception(lang('That loginid has already been taken'),999); + } + if (!$data['account_members'] && !$this->account) + { + throw new Exception(lang('You must select at least one group member.'),9); + } + if ($data['account_members']) + { + $data['account_members'] = admin_cmd::parse_accounts($data['account_members'],true); + } + if ($check_only) return true; + + if ($this->account) + { + if (!($old = admin_cmd::$accounts->read($data['account_id']))) + { + throw new Exception(lang("Unknown account: %1 !!!",$this->account),15); + } + // as the current account class always sets all values, we have to add the not specified ones + foreach($data as $name => &$value) + { + if (is_null($value)) $value = $old[$name]; + } + } + if (!($data['account_id'] = admin_cmd::$accounts->save($data))) + { + //_debug_array($data); + throw new Exception(lang("Error saving account!"),11); + } + $GLOBALS['hook_values'] =& $data; + $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( + 'location' => $this->account ? 'editgroup' : 'addgroup' + ),False,True); // called for every app now, not only enabled ones) + + if ($data['account_members']) + { + admin_cmd::$accounts->set_members($data['account_members'],$data['account_id']); + } + return lang("Account %1 %2",$this->account ? $this->account : $data['account_lid'], + $this->account ? lang('updated') : lang("created with id #%1",$data['account_id'])); + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + return lang('%1 group %2',$this->account ? lang('Edit') : lang('Add'), + admin_cmd::display_account($this->account ? $this->account : $this->set['account_lid'])); + } +} diff --git a/admin/inc/class.admin_cmd_edit_user.inc.php b/admin/inc/class.admin_cmd_edit_user.inc.php new file mode 100644 index 0000000000..1a52b998cc --- /dev/null +++ b/admin/inc/class.admin_cmd_edit_user.inc.php @@ -0,0 +1,202 @@ + + * @package admin + * @copyright (c) 2007 by Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ + +/** + * admin command: edit/add a user + */ +class admin_cmd_edit_user extends admin_cmd_change_pw +{ + /** + * Constructor + * + * @param string/int/array $account account name or id (!$account to add a new account), or array with all parameters + * @param array $set=null array with all data to change + * @param string $password=null password + */ + function __construct($account,$set=null,$password=null) + { + if (!is_array($account)) + { + $account = array( + 'account' => $account, + 'set' => $set, + 'password' => is_null($password) ? $set['account_passwd'] : $password, + ); + } + admin_cmd::__construct($account); + } + + /** + * change the password of a given user + * + * @param boolean $check_only=false only run the checks (and throw the exceptions), but not the command itself + * @return string success message + * @throws Exception(lang("Permission denied !!!"),2) + * @throws Exception(lang("Unknown account: %1 !!!",$this->account),15); + * @throws Exception(lang('Error changing the password for %1 !!!',$this->account),99); + */ + protected function exec($check_only=false) + { + // check creator is still admin and not explicitly forbidden to edit accounts/groups + if ($this->creator) $this->_check_admin('account_access',$this->account ? 16 : 4); + + admin_cmd::_instanciate_accounts(); + + $data = $this->set; + $data['account_type'] = 'u'; + + if ($this->account) // existing account + { + $data['account_id'] = admin_cmd::parse_account($this->account); + } + if (!$data['account_lid'] && (!$this->account || !is_null($data['account_lid']))) + { + throw new Exception(lang('You must enter a loginid'),9); + } + if (!$data['account_lastname'] && (!$this->account || !is_null($data['account_lastname']))) + { + throw new Exception(lang('You must enter a lastname'),9); + } + if (!is_null($data['account_lid']) && ($id = admin_cmd::$accounts->name2id($data['account_lid'],'account_lid','u')) && + $id !== $data['account_id']) + { + throw new Exception(lang('That loginid has already been taken'),999); + } + if (isset($data['account_passwd_2']) && $data['account_passwd'] != $data['account_passwd_2']) + { + throw new Exception(lang('The two passwords are not the same'),0); + } + $data['account_expires'] = $expires = self::_parse_expired($data['account_expires'],(boolean)$this->account); + $data['account_status'] = is_null($expires) ? null : ($expires == -1 || $expires > time() ? 'A' : ''); + + $data['changepassword'] = admin_cmd::parse_boolean($data['changepassword'],$this->account ? null : true); + $data['anonymous'] = admin_cmd::parse_boolean($data['anonymous'],$this->account ? null : false); + + if (!$data['account_primary_group'] && $this->account) + { + $data['account_primary_group'] = null; // dont change + } + else + { + if (!$data['account_primary_group'] && admin_cmd::$accounts->exists('Default') == 2) + { + $data['account_primary_group'] = 'Default'; + } + $data['account_primary_group'] = admin_cmd::parse_account($data['account_primary_group'],false); + } + if (!$data['account_groups'] && $this->account) + { + $data['account_groups'] = null; // dont change + } + else + { + if (!$data['account_groups'] && admin_cmd::$accounts->exists('Default') == 2) + { + $data['account_groups'] = array('Default'); + } + $data['account_groups'] = admin_cmd::parse_accounts($data['account_groups'],false); + } + if ($check_only) return true; + + if ($this->account) + { + if (!($old = admin_cmd::$accounts->read($data['account_id']))) + { + throw new Exception(lang("Unknown account: %1 !!!",$this->account),15); + } + // as the current account class always sets all values, we have to add the not specified ones + foreach($data as $name => &$value) + { + if (is_null($value)) $value = $old[$name]; + } + } + if (!($data['account_id'] = admin_cmd::$accounts->save($data))) + { + //_debug_array($data); + throw new Exception(lang("Error saving account!"),11); + } + if ($data['account_groups']) + { + admin_cmd::$accounts->set_memberships($data['account_groups'],$data['account_id']); + } + if (!is_null($data['anonymous'])) + { + admin_cmd::_instanciate_acl(); + if ($data['anonymous']) + { + admin_cmd::$acl->add_repository('phpgwapi','anonymous',$data['account_id'],1); + } + else + { + admin_cmd::$acl->delete_repository('phpgwapi','anonymous',$data['account_id']); + } + } + if (!is_null($data['changepassword'])) + { + if (!$data['changepassword']) + { + admin_cmd::$acl->add_repository('preferences','nopasswordchange',$data['account_id'],1); + } + else + { + admin_cmd::$acl->delete_repository('preferences','nopasswordchange',$data['account_id']); + } + } + // for existing accounts we have to change the password explicitly (at least that's what the old UI does) + if($this->account && !is_null($this->password)) + { + admin_cmd_change_pw::exec(); // calling the exec method of the admin_cmd_change_pw + } + $data['account_passwd'] = $this->password; + $GLOBALS['hook_values'] =& $data; + $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( + 'location' => $this->account ? 'editaccount' : 'addaccount' + ),False,True); // called for every app now, not only enabled ones) + + return lang("Account %1 %2",$this->account ? $this->account : $data['account_lid'], + $this->account ? lang('updated') : lang("created with id #%1",$data['account_id'])); + } + + /** + * Return a title / string representation for a given command, eg. to display it + * + * @return string + */ + function __tostring() + { + return lang('%1 user %2',$this->account ? lang('Edit') : lang('Add'), + admin_cmd::display_account($this->account ? $this->account : $this->set['account_lid'])); + } + + /** + * parse the expired string and return the expired date as timestamp + * + * @param string $str date, 'never', 'already' or '' (=dont change, or default of never of new accounts) + * @param boolean $exists + * @return int timestamp, 0 for already, -1 for never or null for dont change + * @throws Exception(lang('Invalid formated date "%1"!',$datein),6); + */ + private function _parse_expired($str,$existing) + { + switch($str) + { + case '': + if ($existing) return null; + // fall through --> default for new accounts is never + case 'never': + return -1; + case 'already': + return 0; + } + return admin_cmd::parse_date($str); + } +} diff --git a/admin/inc/class.admin_cmds.inc.php b/admin/inc/class.admin_cmds.inc.php index a6690762d4..801845c754 100644 --- a/admin/inc/class.admin_cmds.inc.php +++ b/admin/inc/class.admin_cmds.inc.php @@ -10,9 +10,6 @@ * @version $Id$ */ -require_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd.inc.php'); -require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.etemplate.inc.php'); - /** * UI for the admin comand queue */ @@ -31,7 +28,7 @@ class admin_cmds * @param array &$readonlys * @return int */ - static function get_rows($query,&$rows,&$readonlys) + static function get_rows(array $query,&$rows,&$readonlys) { $GLOBALS['egw']->session->appsession('cmds','admin',$query); @@ -103,7 +100,7 @@ class admin_cmds * @param array &$readonlys * @return int */ - static function get_remotes($query,&$rows,&$readonlys) + static function get_remotes(array $query,&$rows,&$readonlys) { return admin_cmd::get_remotes($query,$rows,$readonlys); }