egroupware_official/addressbook/inc/class.addressbook_ui.inc.php
Nathan Gray a5fcb430ef Only load infolog list on first time.
Loading it again (in an ajax submit) will load it into addressbook.view etemplate2 object used as context for request, corrupting it.
2014-03-11 15:26:09 +00:00

2572 lines
89 KiB
PHP

<?php
/**
* EGroupware - Addressbook - user interface
*
* @link www.egroupware.org
* @author Cornelius Weiss <egw@von-und-zu-weiss.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005-13 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005/6 by Cornelius Weiss <egw@von-und-zu-weiss.de>
* @package addressbook
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* General user interface object of the adressbook
*/
class addressbook_ui extends addressbook_bo
{
public $public_functions = array(
'search' => True,
'edit' => True,
'view' => True,
'index' => True,
'photo' => True,
'emailpopup'=> True,
'migrate2ldap' => True,
'admin_set_fileas' => True,
'admin_set_all_cleanup' => True,
'cat_add' => True,
);
protected $org_views;
/**
* Addressbook configuration (stored as phpgwapi = general server config)
*
* @var array
*/
protected $config;
/**
* Fields to copy, default if nothing specified in config
*
* @var array
*/
static public $copy_fields = array(
'org_name',
'org_unit',
'adr_one_street',
'adr_one_street2',
'adr_one_locality',
'adr_one_region',
'adr_one_postalcode',
'adr_one_countryname',
'adr_one_countrycode',
'email',
'url',
'tel_work',
'cat_id'
);
/**
* Instance of eTemplate class
*
* @var etemplate_new
*/
protected $tmpl;
/**
* Constructor
*
* @param string $contact_app
*/
function __construct($contact_app='addressbook')
{
parent::__construct($contact_app);
$this->tmpl = new etemplate_new();
$this->org_views = array(
'org_name' => lang('Organisations'),
'org_name,adr_one_locality' => lang('Organisations by location'),
'org_name,org_unit' => lang('Organisations by departments'),
);
// make sure the hook for export_limit is registered
if (!$GLOBALS['egw']->hooks->hook_exists('export_limit','addressbook')) $GLOBALS['egw']->hooks->register_single_app_hook('addressbook','export_limit');
$this->config =& $GLOBALS['egw_info']['server'];
// check if a contact specific export limit is set, if yes use it also for etemplate's csv export
$this->config['export_limit'] = $this->config['contact_export_limit'] = bo_merge::getExportLimit($app='addressbook');
if ($this->config['copy_fields'] && ($fields = is_array($this->config['copy_fields']) ?
$this->config['copy_fields'] : unserialize($this->config['copy_fields'])))
{
// Set country code if country name is selected
$supported_fields = $this->get_fields('supported',null,0);
if(in_array('adr_one_countrycode', $supported_fields) && in_array('adr_one_countryname',$fields))
{
$fields[] = 'adr_one_countrycode';
}
if(in_array('adr_two_countrycode', $supported_fields) && in_array('adr_two_countryname',$fields))
{
$fields[] = 'adr_two_countrycode';
}
self::$copy_fields = $fields;
}
}
/**
* List contacts of an addressbook
*
* @param array $content=null submitted content
* @param string $msg=null message to show
* @param boolean $do_email=false do an email-selection popup or the regular index-page
*/
function index($content=null,$msg=null,$do_email=false)
{
//echo "<p>uicontacts::index(".print_r($content,true).",'$msg')</p>\n";
if (($re_submit = is_array($content)))
{
$do_email = $content['do_email'];
if (isset($content['nm']['rows']['delete'])) // handle a single delete like delete with the checkboxes
{
list($id) = @each($content['nm']['rows']['delete']);
$content['nm']['action'] = 'delete';
$content['nm']['selected'] = array($id);
}
if (isset($content['nm']['rows']['document'])) // handle insert in default document button like an action
{
list($id) = @each($content['nm']['rows']['document']);
$content['nm']['action'] = 'document';
$content['nm']['selected'] = array($id);
}
if ($content['nm']['action'] !== '')
{
if (!count($content['nm']['selected']) && !$content['nm']['select_all'] && $content['nm']['action'] != 'delete_list')
{
$msg = lang('You need to select some contacts first');
}
elseif ($content['nm']['action'] == 'view') // org-view via context menu
{
$content['nm']['org_view'] = array_shift($content['nm']['selected']);
}
else
{
if ($this->action($content['nm']['action'],$content['nm']['selected'],$content['nm']['select_all'],
$success,$failed,$action_msg,$content['do_email'] ? 'email' : 'index',$msg,$content['nm']['checkboxes']))
{
$msg .= lang('%1 contact(s) %2',$success,$action_msg);
egw_framework::message($msg);
}
elseif(is_null($msg))
{
$msg .= lang('%1 contact(s) %2, %3 failed because of insufficent rights !!!',$success,$action_msg,$failed);
egw_framework::message($msg,'error');
}
$msg = '';
}
}
if ($content['nm']['rows']['infolog'])
{
list($org) = each($content['nm']['rows']['infolog']);
return $this->infolog_org_view($org);
}
if ($content['nm']['rows']['view']) // show all contacts of an organisation
{
list($org_view) = each($content['nm']['rows']['view']);
}
else
{
$org_view = $content['nm']['org_view'];
}
$typeselection = $content['nm']['col_filter']['tid'];
}
elseif($_GET['add_list'])
{
$list = $this->add_list($_GET['add_list'],$_GET['owner']?$_GET['owner']:$this->user);
if ($list === true)
{
$msg = lang('List already exists!');
}
elseif ($list)
{
$msg = lang('List created');
}
else
{
$msg = lang('List creation failed, no rights!');
}
}
$preserv = array(
'do_email' => $do_email,
);
$to = $content['nm']['to'];
$content = array();
if($msg || $_GET['msg'])
{
egw_framework::message($msg ? $msg : $_GET['msg']);
}
$content['nm'] = egw_session::appsession($do_email ? 'email' : 'index','addressbook');
if (!is_array($content['nm']))
{
$content['nm'] = array(
'get_rows' => 'addressbook.addressbook_ui.get_rows', // I method/callback to request the data for the rows eg. 'notes.bo.get_rows'
'bottom_too' => false, // I show the nextmatch-line (arrows, filters, search, ...) again after the rows
'never_hide' => True, // I never hide the nextmatch-line if less then maxmatch entrie
'start' => 0, // IO position in list
'cat_id' => '', // IO category, if not 'no_cat' => True
'search' => '', // IO search pattern
'order' => 'n_family', // IO name of the column to sort after (optional for the sortheaders)
'sort' => 'ASC', // IO direction of the sort: 'ASC' or 'DESC'
'col_filter' => array(), // IO array of column-name value pairs (optional for the filterheaders)
'filter_label' => lang('Addressbook'), // I label for filter (optional)
'filter' => '', // =All // IO filter, if not 'no_filter' => True
'filter_no_lang' => True, // I set no_lang for filter (=dont translate the options)
'no_filter2' => True, // I disable the 2. filter (params are the same as for filter)
'filter2_label' => lang('Distribution lists'), // IO filter2, if not 'no_filter2' => True
'filter2' => '', // IO filter2, if not 'no_filter2' => True
'filter2_no_lang'=> True, // I set no_lang for filter2 (=dont translate the options)
'lettersearch' => true,
'do_email' => $do_email ? 1 : 0,
'default_cols' => '!cat_id,contact_created_contact_modified,distribution_list,contact_id,owner,legacy_actions',
'filter2_onchange' => "app.addressbook.filter2_onchange();",
'manual' => $do_email ? ' ' : false, // space for the manual icon
//'actions' => $this->get_actions(), // set on each request, as it depends on some filters
'row_id' => 'id',
'favorites' => true,
'placeholder_actions' => array('add')
);
if ($do_email)
{
$content['nm']['filter2_onchange'] = 'app.addressbook.filter2_onchange_email();';
}
// use the state of the last session stored in the user prefs
if (($state = @unserialize($this->prefs[$do_email ? 'email_state' : 'index_state'])))
{
$content['nm'] = array_merge($content['nm'],$state);
}
}
$content['nm']['options-cat_id'] = array(lang('none'));
// Delete list action depends on permissions
if($this->get_lists(EGW_ACL_EDIT))
{
$content['nm']['placeholder_actions'][] = 'delete_list';
}
// Search parameter passed in
if ($_GET['search']) {
$content['nm']['search'] = $_GET['search'];
}
if (isset($typeselection)) $content['nm']['col_filter']['tid'] = $typeselection;
// save the tid for use in creating new addressbook entrys via UI. Current tid is to be used as type of new entrys
//error_log(__METHOD__.__LINE__.' '.$content['nm']['col_filter']['tid']);
egw_cache::setSession('addressbook','active_tid',$content['nm']['col_filter']['tid']);
if ($this->lists_available())
{
$sel_options['filter2'] = $this->get_lists(EGW_ACL_READ,array('' => lang('none')));
$sel_options['filter2']['add'] = lang('Add a new list').'...'; // put it at the end
}
if ($do_email)
{
if (!$re_submit)
{
$content['nm']['to'] = 'to'; // use 'bcc' if you want bcc as preselected standard mailaddress scope
$content['nm']['email_type'] = $this->prefs['distributionListPreferredMail'] ? $this->prefs['distributionListPreferredMail'] : 'email';
$content['nm']['search'] = '@';
}
else
{
$content['nm']['to'] = $to;
$content['nm']['email_type'] = $this->prefs['distributionListPreferredMail'] ? $this->prefs['distributionListPreferredMail'] : 'email';
}
$content['nm']['header_left'] = 'addressbook.email.left';
}
// Organisation stuff is not (yet) availible with ldap
elseif($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap')
{
$content['nm']['header_left'] = 'addressbook.index.left';
}
$sel_options['filter'] = $sel_options['owner'] = $this->get_addressbooks(EGW_ACL_READ,lang('All'));
$sel_options['to'] = array(
'to' => 'To',
'cc' => 'Cc',
'bcc' => 'Bcc',
);
// if there is any export limit set, pass it on to the nextmatch, to be evaluated by the export
if (isset($this->config['contact_export_limit']) && (int)$this->config['contact_export_limit']) $content['nm']['export_limit']=$this->config['contact_export_limit'];
// dont show tid-selection if we have only one content_type
// be a bit more sophisticated about it
$content['nm']['header_right'] = 'addressbook.index.right_add';
$availabletypes = array_keys($this->content_types);
if ($content['nm']['col_filter']['tid'] && !in_array($content['nm']['col_filter']['tid'],$availabletypes))
{
//_debug_array(array('Typefilter:'=> $content['nm']['col_filter']['tid'],'Available Types:'=>$availabletypes,'action:'=>'remove invalid filter'));
unset($content['nm']['col_filter']['tid']);
}
if (!isset($content['nm']['col_filter']['tid'])) $content['nm']['col_filter']['tid'] = $availabletypes[0];
if (count($this->content_types) > 1)
{
$content['nm']['header_right'] = 'addressbook.index.right_addplus';
foreach($this->content_types as $tid => $data)
{
$sel_options['tid'][$tid] = $data['name'];
}
}
// get the availible org-views plus the label of the contacts view of one org
$sel_options['org_view'] = $this->org_views;
if (isset($org_view)) $content['nm']['org_view'] = $org_view;
$content['nm']['actions'] = $this->get_actions($content['nm']['col_filter']['tid'], $content['nm']['org_view']);
if (!isset($sel_options['org_view'][(string) $content['nm']['org_view']]))
{
$sel_options['org_view'] += $this->_get_org_name((string)$content['nm']['org_view']);
}
// unset the filters regarding organisations, when there is no organisation selected
if (empty($sel_options['org_view'][(string) $content['nm']['org_view']]) || stripos($org_view,":") === false )
{
unset($content['nm']['col_filter']['org_name']);
unset($content['nm']['col_filter']['org_unit']);
unset($content['nm']['col_filter']['adr_one_locality']);
}
$content['nm']['org_view_label'] = $sel_options['org_view'][(string) $content['nm']['org_view']];
$this->tmpl->read($do_email ? 'addressbook.email' : 'addressbook.index');
return $this->tmpl->exec($do_email ? 'addressbook.addressbook_ui.emailpopup' : 'addressbook.addressbook_ui.index',
$content,$sel_options,$readonlys,$preserv,$do_email ? 2 : 0);
}
/**
* Get actions / context menu items
*
* @param string $tid_filter=null
* @param string $org_view=null
* @return array see nextmatch_widget::get_actions()
*/
private function get_actions($tid_filter=null, $org_view=null)
{
// we have no org view (view of one org has context menu like regular "add contacts" view, as it shows contacts
if (!isset($this->org_views[(string) $org_view]))
{
$actions = array(
'view' => array(
'caption' => 'View',
'default' => true,
'allowOnMultiple' => false,
'url' => 'menuaction=addressbook.addressbook_ui.view&contact_id=$id&ajax=true',
'popup' => egw_link::get_registry('addressbook', 'view_popup'),
'group' => $group=1,
),
'edit' => array(
'caption' => 'Edit',
'allowOnMultiple' => false,
'url' => 'menuaction=addressbook.addressbook_ui.edit&contact_id=$id',
'popup' => egw_link::get_registry('addressbook', 'add_popup'),
'group' => $group,
'disableClass' => 'rowNoEdit',
),
'add' => array(
'caption' => 'Add',
'group' => $group,
'children' => array(
'new' => array(
'caption' => 'New',
'url' => 'menuaction=addressbook.addressbook_ui.edit',
'popup' => egw_link::get_registry('addressbook', 'add_popup'),
'icon' => 'new',
),
'copy' => array(
'caption' => 'Copy',
'url' => 'menuaction=addressbook.addressbook_ui.edit&makecp=1&contact_id=$id',
'popup' => egw_link::get_registry('addressbook', 'add_popup'),
'allowOnMultiple' => false,
'icon' => 'copy',
),
),
),
);
}
else // org view
{
$actions = array(
'view' => array(
'caption' => 'View',
'default' => true,
'allowOnMultiple' => false,
'group' => $group=1,
),
'add' => array(
'caption' => 'Add',
'group' => $group,
'allowOnMultiple' => false,
'url' => 'menuaction=addressbook.addressbook_ui.edit&org=$id',
'popup' => egw_link::get_registry('addressbook', 'add_popup'),
),
);
}
++$group; // other AB related stuff group: lists, AB's, categories
// categories submenu
$actions['cat'] = array(
'caption' => 'Categories',
'group' => $group,
'children' => array(
'cat_add' => nextmatch_widget::category_action(
'addressbook',$group,'Add category', 'cat_add_',
true, 0,nextmatch_widget::DEFAULT_MAX_MENU_LENGTH,false
)+array(
'icon' => 'foldertree_nolines_plus',
'disableClass' => 'rowNoEdit',
),
'cat_del' => nextmatch_widget::category_action(
'addressbook',$group,'Delete category', 'cat_del_',
true, 0,nextmatch_widget::DEFAULT_MAX_MENU_LENGTH,false
)+array(
'icon' => 'foldertree_nolines_minus',
'disableClass' => 'rowNoEdit',
),
),
);
if (!$GLOBALS['egw_info']['user']['apps']['preferences']) unset($actions['cats']['children']['cat_edit']);
// Submenu for all distributionlist stuff
$actions['lists'] = array(
'caption' => 'Distribution lists',
'children' => array(
'list_add' => array(
'caption' => 'Add a new list',
'icon' => 'new',
'onExecute' => 'javaScript:app.addressbook.add_new_list',
),
),
'group' => $group,
);
if (($add_lists = $this->get_lists(EGW_ACL_EDIT))) // do we have distribution lists?, and are we allowed to edit them
{
$actions['lists']['children'] += array(
'to_list' => array(
'caption' => 'Add to distribution list',
'children' => $add_lists,
'prefix' => 'to_list_',
'icon' => 'foldertree_nolines_plus',
'enabled' => ($add_lists?true:false), // if there are editable lists, allow to add a contact to one of them,
//'disableClass' => 'rowNoEdit', // wether you are allowed to edit the contact or not, as you alter a list, not the contact
),
'remove_from_list' => array(
'caption' => 'Remove from distribution list',
'confirm' => 'Remove selected contacts from distribution list',
'icon' => 'foldertree_nolines_minus',
'enabled' => 'javaScript:app.addressbook.nm_compare_field',
'fieldId' => 'exec[nm][filter2]',
'fieldValue' => '!', // enable if list != ''
),
'delete_list' => array(
'caption' => 'Delete selected distribution list!',
'confirm' => 'Delete selected distribution list!',
'icon' => 'delete',
'enabled' => 'javaScript:app.addressbook.nm_compare_field',
'fieldId' => 'exec[nm][filter2]',
'fieldValue' => '!', // enable if list != ''
),
);
if(is_subclass_of('etemplate', 'etemplate_new'))
{
$actions['lists']['children']['remove_from_list']['fieldId'] = 'filter2';
$actions['lists']['children']['delete_list']['fieldId'] = 'filter2';
}
}
// move to AB
if (($move2addressbooks = $this->get_addressbooks(EGW_ACL_ADD))) // do we have addressbooks, we should
{
foreach($move2addressbooks as $owner => $label)
{
$this->type_icon((int)$owner, substr($owner,-1) == 'p', 'n', $icon, $type_label);
$move2addressbooks[$owner] = array(
'icon' => $icon,
'caption' => $label,
);
}
// copy checkbox
$move2addressbooks= array(
'copy' =>array(
'id' => 'move_to_copy',
'caption' => 'Copy instead of move',
'checkbox' => true,
)) + $move2addressbooks;
$actions['move_to'] = array(
'caption' => 'Move to addressbook',
'children' => $move2addressbooks,
'prefix' => 'move_to_',
'group' => $group,
'disableClass' => 'rowNoDelete',
);
}
$actions['merge'] = array(
'caption' => 'Merge contacts',
'confirm' => 'Merge into first or account, deletes all other!',
'hint' => 'Merge into first or account, deletes all other!',
'allowOnMultiple' => 'only',
'group' => $group,
);
++$group; // integration with other apps: infolog, calendar, filemanager
if ($GLOBALS['egw_info']['user']['apps']['infolog'])
{
$actions['infolog_app'] = array(
'caption' => 'InfoLog',
'icon' => 'infolog/navbar',
'group' => $group,
'children' => array(
'infolog' => array(
'caption' => lang('View linked InfoLog entries'),
'icon' => 'infolog/navbar',
'onExecute' => 'javaScript:app.addressbook.view_infolog',
'allowOnMultiple' => false
),
'infolog_add' => array(
'caption' => 'Add a new Infolog',
'icon' => 'new',
'url' => 'menuaction=infolog.infolog_ui.edit&type=task&action=addressbook&action_id=$id',
'popup' => egw_link::get_registry('infolog', 'add_popup'),
'onExecute' => 'javaScript:app.addressbook.add_task', // call server for org-view only
),
)
);
}
if ($GLOBALS['egw_info']['user']['apps']['calendar'])
{
$actions['calendar'] = array(
'icon' => 'calendar/navbar',
'caption' => 'Calendar',
'group' => $group,
'children' => array(
'calendar_view' => array(
'caption' => 'Show',
'icon' => 'view',
'url' => 'menuaction=calendar.calendar_uilist.listview&filter=all&owner=0,c$id&ajax=true',
'targetapp' => 'calendar', // open in calendar tab
),
'calendar_add' => array(
'caption' => 'Add appointment',
'icon' => 'new',
'url' => 'menuaction=calendar.calendar_uiforms.edit&participants=c$id',
'popup' => egw_link::get_registry('calendar', 'add_popup'),
'onExecute' => 'javaScript:app.addressbook.add_cal', // call server for org-view only
),
),
);
}
//Send to email
$actions['email'] = array(
'caption' => 'Email',
'icon' => 'mail/navbar',
'group' => $group,
'children' => array(
'add_to_to' => array(
'caption' => lang('Add to To'),
'no_lang' => true,
'onExecute' => 'javaScript:app.addressbook.addEmail',
),
'add_to_cc' => array(
'caption' => lang('Add to Cc'),
'no_lang' => true,
'onExecute' => 'javaScript:app.addressbook.addEmail',
),
'add_to_bcc' => array(
'caption' => lang('Add to BCc'),
'no_lang' => true,
'onExecute' => 'javaScript:app.addressbook.addEmail',
),
'email_business' => array(
'caption' => lang('Business email'),
'no_lang' => true,
'checkbox' => true,
'group' => $group,
'onExecute' => 'javaScript:app.addressbook.mailCheckbox',
'checked' => $this->prefs['preferredMail']['business'],
),
'email_home' => array(
'caption' => lang('Home email'),
'no_lang' => true,
'checkbox' => true,
'group' => $group,
'onExecute' => 'javaScript:app.addressbook.mailCheckbox',
'checked' => $this->prefs['preferredMail']['private'],
),
),
);
if (!$this->prefs['preferredMail'])
$actions['email']['children']['email_business']['checked'] = true;
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{
$actions['filemanager'] = array(
'icon' => 'filemanager/navbar',
'caption' => 'Filemanager',
'url' => 'menuaction=filemanager.filemanager_ui.index&path=/apps/addressbook/$id&ajax=true',
'allowOnMultiple' => false,
'group' => $group,
// disable for for org-views, as it needs contact-ids
'enabled' => !isset($this->org_views[(string) $org_view]),
);
}
// check if user is an admin or the export is not generally turned off (contact_export_limit is non-numerical, eg. no)
$exception = bo_merge::is_export_limit_excepted();
if ((isset($GLOBALS['egw_info']['user']['apps']['admin']) || $exception) || !$this->config['contact_export_limit'] || (int)$this->config['contact_export_limit'])
{
$actions['export'] = array(
'caption' => 'Export',
'icon' => 'filesave',
'group' => ++$group,
'children' => array(
'csv' => array(
'caption' => 'Export as CSV',
'allowOnMultiple' => true,
'url' => 'menuaction=importexport.importexport_export_ui.export_dialog&appname=addressbook&plugin=addressbook_export_contacts_csv&selection=$id',
'popup' => '850x440'
),
'vcard' => array(
'caption' => 'Export as VCard',
'postSubmit' => true, // download needs post submit (not Ajax) to work
'icon' => egw_vfs::mime_icon('text/vcard'),
),
),
);
}
$actions['documents'] = addressbook_merge::document_action(
$this->prefs['document_dir'], $group, 'Insert in document', 'document_',
$this->prefs['default_document'], $this->config['contact_export_limit']
);
if (isset($GLOBALS['egw_info']['apps']['stylite']) && $GLOBALS['egw_info']['user']['apps']['felamimail'])
{
$actions['mail'] = array(
'caption' => lang('Mail VCard'),
'icon' => 'filemanager/mail_post_to',
'group' => $group,
'onExecute' => 'javaScript:app.addressbook.adb_mail_vcard',
);
}
++$group;
if (!($tid_filter == 'D' && !$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge'))
{
$actions['delete'] = array(
'caption' => 'Delete',
'confirm' => 'Delete this contact',
'confirm_multiple' => 'Delete these entries',
'group' => $group,
'disableClass' => 'rowNoDelete',
);
}
if($tid_filter == 'D')
{
$actions['undelete'] = array(
'caption' => 'Un-delete',
'icon' => 'revert',
'group' => $group,
'disableClass' => 'rowNoEdit',
);
}
if (isset($actions['export']['children']['csv']) && !importexport_helper_functions::has_definitions('addressbook','export')) unset($actions['export']['children']['csv']);
//echo "<p>".__METHOD__."($do_email, $tid_filter, $org_view)</p>\n"; _debug_array($actions);
// Allow contacts to be dragged
/*
$actions['drag'] = array(
'type' => 'drag',
'dragType' => 'addressbook'
);
*/
return $actions;
}
/**
* Get the name of an organization from an ID for the org_view filter
*
* @param string $org
* @return Array ID => name
*/
private function _get_org_name($org)
{
$org_name = array();
if (strpos($org,'*AND*')!== false) $org = str_replace('*AND*','&',$org);
foreach(explode('|||',$org) as $part)
{
list(,$name) = explode(':',$part,2);
if ($name) $org_name[] = $name;
}
$org_name = implode(': ',$org_name);
return array($org => $org_name);
}
/**
* Email address-selection popup
*
* @param array $content=null submitted content
* @param string $msg=null message to show
*/
function emailpopup($content=null,$msg=null)
{
if (strpos($GLOBALS['egw_info']['flags']['java_script'],'addEmail') === false)
{
$handler = 'opener.addEmail(to,email)';
$GLOBALS['egw_info']['flags']['java_script'].= "
<script>
window.egw_LAB.wait(function() {
window.focus();
window.addEmail = function(email)
{
var to = 'to';
splitter = email.indexOf(' <');
namepart = email.substring(0,splitter);
emailpart = email.substring(splitter);
email = namepart.replace(/@/g,' ')+emailpart;
if (document.getElementById('exec[nm][to][cc]').checked == true)
{
to = 'cc';
}
else
{
if (document.getElementById('exec[nm][to][bcc]').checked == true)
{
to = 'bcc';
}
}
$handler;
};
});
</script>
";
}
return $this->index($content,$msg,true);
}
/**
* Show the infologs of an whole organisation
*
* @param string $org
*/
function infolog_org_view($org)
{
$query = egw_session::appsession('index','addressbook');
$query['num_rows'] = -1; // all
$query['org_view'] = $org;
$query['searchletter'] = '';
$this->get_rows($query,$checked,$readonlys,true); // true = only return the id's
if (count($checked) > 1) // use a nicely formatted org-name as title in infolog
{
$parts = array();
if (strpos($org,'*AND*')!== false) $org = str_replace('*AND*','&',$org);
foreach(explode('|||',$org) as $part)
{
list(,$part) = explode(':',$part,2);
if ($part) $parts[] = $part;
}
$org = implode(', ',$parts);
}
else
{
$org = ''; // use infolog default of link-title
}
egw::redirect_link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.index',
'action' => 'addressbook',
'action_id' => implode(',',$checked),
'action_title' => $org,
));
}
function ajax_add_whole_list($list, $email_type = 'email')
{
$query = egw_session::appsession('email','addressbook');
$query['filter2'] = (int)$list;
$this->action($email_type,array(),true,$success,$failed,$action_msg,$query,$msg);
$response = egw_json_response::get();
if ($success) $response->addScript(egw_framework::set_onload(''));
// close window only if no errors AND something added
if ($failed || !$success)
{
if (!$msg) $msg = $failed ? lang('%1 contact(s) %2, %3 failed because of insufficent rights !!!',$success,$action_msg,$failed) :
lang('%1 contact(s) %2',$success,$action_msg);
$response->addScript("alert('".addslashes($msg)."')");
// reset the filter
$response->addScript("document.getElementById('exec[nm][filter2]').value='';");
}
else
{
if (!$msg) $msg = lang('%1 contact(s) %2',$success,$action_msg);
$response->addScript("alert('".addslashes($msg)."')");
$response->addScript('window.close();');
}
}
/**
* apply an action to multiple contacts
*
* @param string/int $action 'delete', 'vcard', 'csv' or nummerical account_id to move contacts to that addessbook
* @param array $checked contact id's to use if !$use_all
* @param boolean $use_all if true use all contacts of the current selection (in the session)
* @param int &$success number of succeded actions
* @param int &$failed number of failed actions (not enought permissions)
* @param string &$action_msg translated verb for the actions, to be used in a message like %1 contacts 'deleted'
* @param string/array $session_name 'index' or 'email', or array with session-data depending if we are in the main list or the popup
* @return boolean true if all actions succeded, false otherwise
*/
function action($action,$checked,$use_all,&$success,&$failed,&$action_msg,$session_name,&$msg, $checkboxes = NULL)
{
//echo "<p>uicontacts::action('$action',".print_r($checked,true).','.(int)$use_all.",...)</p>\n";
$success = $failed = 0;
if ($use_all || in_array($action,array('remove_from_list','delete_list')))
{
// get the whole selection
$query = is_array($session_name) ? $session_name : egw_session::appsession($session_name,'addressbook');
if ($use_all)
{
@set_time_limit(0); // switch off the execution time limit, as it's for big selections to small
$query['num_rows'] = -1; // all
$this->get_rows($query,$checked,$readonlys,true); // true = only return the id's
}
}
// replace org_name:* id's with all id's of that org
$org_contacts = array();
foreach((array)$checked as $n => $id)
{
if (substr($id,0,9) == 'org_name:')
{
if (count($checked) == 1 && !count($org_contacts) && $action == 'infolog')
{
return $this->infolog_org_view($id); // uses the org-name, instead of 'selected contacts'
}
unset($checked[$n]);
$query = egw_session::appsession($session_name,'addressbook');
$query['num_rows'] = -1; // all
$query['org_view'] = $id;
unset($query['filter2']);
$this->get_rows($query,$extra,$readonlys,true); // true = only return the id's
if ($extra[0]) $org_contacts = array_merge($org_contacts,$extra);
}
}
if ($org_contacts) $checked = array_unique($checked ? array_merge($checked,$org_contacts) : $org_contacts);
//_debug_array($checked); exit;
if (substr($action,0,8) == 'move_to_')
{
$action = (int)substr($action,8).(substr($action,-1) == 'p' ? 'p' : '');
}
elseif (substr($action,0,8) == 'to_list_')
{
$to_list = (int)substr($action,8);
$action = 'to_list';
}
elseif (substr($action,0,9) == 'document_')
{
$document = substr($action,9);
$action = 'document';
}
elseif(substr($action,0,4) == 'cat_') // cat_add_123 or cat_del_456
{
$cat_id = (int)substr($action, 8);
$action = substr($action,0,7);
}
// Security: stop non-admins to export more then the configured number of contacts
if (in_array($action,array('csv','vcard')) && $this->config['contact_export_limit'] && !bo_merge::is_export_limit_excepted() &&
(!is_numeric($this->config['contact_export_limit']) || count($checked) > $this->config['contact_export_limit']))
{
$action_msg = lang('exported');
$failed = count($checked);
return false;
}
switch($action)
{
case 'vcard':
$action_msg = lang('exported');
$vcard = new addressbook_vcal('addressbook','text/vcard');
$vcard->export($checked);
// does not return!
$Ok = false;
break;
case 'merge':
$success = $this->merge($checked,$error_msg);
$failed = count($checked) - (int)$success;
$action_msg = lang('merged');
$checked = array(); // to not start the single actions
break;
case 'delete_list':
if (!$query['filter2'])
{
$msg = lang('You need to select a distribution list');
}
elseif($this->delete_list($query['filter2']) === false)
{
$msg = lang('Insufficent rights to delete this list!');
}
else
{
$msg = lang('Distribution list deleted');
unset($query['filter2']);
egw_session::appsession($session_name,'addressbook',$query);
}
return false;
case 'document':
if (!$document) $document = $this->prefs['default_document'];
$document_merge = new addressbook_merge();
$msg = $document_merge->download($document, $checked, '', $this->prefs['document_dir']);
$failed = count($checked);
return false;
case 'infolog_add':
list($width,$height) = explode('x',egw_link::get_registry('infolog', 'add_popup'));
egw_framework::set_onload(
"egw_openWindowCentered2('".egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.edit',
'type' => 'task',
'action' => 'addressbook',
'action_id' => implode(',',$checked),
))."','_blank',$width,$height);");
$msg = ''; // no message, as we send none in javascript too and users sees opening popup
return false;
case 'calendar_add': // add appointment for org-views, other views are handled directly in javascript
list($width,$height) = explode('x',egw_link::get_registry('calendar', 'add_popup'));
egw_framework::set_onload(
"egw_openWindowCentered2('".egw::link('/index.php',array(
'menuaction' => 'calendar.calendar_uiforms.edit',
'participants' => 'c'.implode(',c',$checked),
))."','_blank',$width,$height);");
$msg = ''; // no message, as we send none in javascript too and users sees opening popup
return false;
case 'calendar_view': // show calendar for org-views, other views are handled directly in javascript
list($width,$height) = explode('x',egw_link::get_registry('calendar', 'add_popup'));
egw::redirect_link('/index.php',array(
'menuaction' => 'calendar.calendar_uiviews.index',
'owner' => 'c'.implode(',c',$checked),
));
}
foreach($checked as $id)
{
switch($action)
{
case 'cat_add':
case 'cat_del':
if (($Ok = !!($contact = $this->read($id)) && $this->check_perms(EGW_ACL_EDIT,$contact)))
{
$action_msg = $action == 'cat_add' ? lang('categorie added') : lang('categorie delete');
$cat_ids = $contact['cat_id'] ? explode(',', $contact['cat_id']) : array(); //existing categories
if ($action == 'cat_add')
{
$cat_ids[] = $cat_id;
$cat_ids = array_unique($cat_ids);
}
elseif ((($key = array_search($cat_id,$cat_ids))) !== false)
{
unset($cat_ids[$key]);
}
$cat_ids = $cat_ids ? implode(',',$cat_ids) : null;
if ($cat_ids !== $contact['cat_id'])
{
$contact['cat_id'] = $cat_ids;
$Ok = $this->save($contact);
}
}
break;
case 'delete':
$action_msg = lang('deleted');
if (($Ok = !!($contact = $this->read($id)) && $this->check_perms(EGW_ACL_DELETE,$contact)))
{
if ($contact['owner'] || // regular contact or
// already deleted account (should no longer happen, but needed to allow for cleanup)
$contact['tid'] == addressbook_so::DELETED_TYPE)
{
$Ok = $this->delete($id, $contact['tid'] != addressbook_so::DELETED_TYPE);
}
// delete single account --> redirect to admin
elseif (count($checked) == 1 && $contact['account_id'])
{
egw::redirect_link('/index.php',array(
'menuaction' => 'admin.uiaccounts.delete_user',
'account_id' => $contact['account_id'],
));
// this does NOT return!
}
else // no mass delete of accounts
{
$Ok = false;
}
}
break;
case 'undelete':
$action_msg = lang('recovered');
if ($contact = $this->read($id))
{
$contact['tid'] = 'n';
$Ok = $this->save($contact);
}
break;
case 'email':
case 'email_home':
error_log(__METHOD__. "() email");
$action == 'email' ? $action_fallback = 'email_home' : $action_fallback = 'email';
$action_msg = lang('added');
if($contact = $this->read($id))
{
if(strpos($contact[$action],'@') !== false)
{
$email = $contact[$action];
}
elseif(strpos($contact[$action_fallback],'@') !== false)
{
$email = $contact[$action_fallback];
}
else
{
$Ok = $email = false;
}
if($email)
{
$contact['n_fn'] = str_replace(array(',','@'),' ',$contact['n_fn']);
egw_framework::set_onload("addEmail('".addslashes(
$contact['n_fn'] ? $contact['n_fn'].' <'.trim($email).'>' : trim($email))."');");
//error_log(__METHOD__.__LINE__."addEmail('".addslashes(
// $contact['n_fn'] ? $contact['n_fn'].' <'.trim($email).'>' : trim($email))."');");
$Ok = true;
}
}
break;
case 'remove_from_list':
$action_msg = lang('removed from distribution list');
if (!$query['filter2'])
{
$msg = lang('You need to select a distribution list');
return false;
}
else
{
$Ok = $this->remove_from_list($id,$query['filter2']) !== false;
}
break;
case 'to_list':
$action_msg = lang('added to distribution list');
if (!$to_list)
{
$msg = lang('You need to select a distribution list');
return false;
}
else
{
$Ok = $this->add2list($id,$to_list) !== false;
}
break;
default: // move to an other addressbook
if (!(int)$action || !($this->grants[(string) (int) $action] & EGW_ACL_EDIT)) // might be ADD in the future
{
return false;
}
if (!$checkboxes['move_to_copy'])
{
$action_msg = lang('moved');
if (($Ok = !!($contact = $this->read($id)) && $this->check_perms(EGW_ACL_DELETE,$contact)))
{
if (!$contact['owner']) // no mass-change of accounts
{
$Ok = false;
}
elseif ($contact['owner'] != (int)$action || $contact['private'] != (int)(substr($action,-1) == 'p'))
{
$contact['owner'] = (int) $action;
$contact['private'] = (int)(substr($action,-1) == 'p');
$Ok = $this->save($contact);
}
}
}
else
{
$action_msg = lang('copied');
if (($Ok = !!($contact = $this->read($id)) && $this->check_perms(EGW_ACL_DELETE,$contact)))
{
if (!$contact['owner']) // no mass-change of accounts
{
$Ok = false;
}
elseif ($contact['owner'] != (int)$action || $contact['private'] != (int)(substr($action,-1) == 'p'))
{
unset($contact['id']);
unset($contact['uid']);
unset($contact['etag']);
$contact['owner'] = (int) $action;
$contact['private'] = (int)(substr($action,-1) == 'p');
$Ok = $this->save($contact);
}
}
}
break;
}
if ($Ok)
{
++$success;
}
elseif ($action != 'email' && $action != 'email_home')
{
++$failed;
}
}
return !$failed;
}
/**
* rows callback for index nextmatch
*
* @internal
* @param array &$query
* @param array &$rows returned rows/cups
* @param array &$readonlys eg. to disable buttons based on acl
* @param boolean $id_only=false if true only return (via $rows) an array of contact-ids, dont save state to session
* @return int total number of contacts matching the selection
*/
function get_rows(&$query,&$rows,&$readonlys,$id_only=false)
{
$do_email = $query['do_email'];
$what = $query['sitemgr_display'] ? $query['sitemgr_display'] : ($do_email ? 'email' : 'index');
if (!$id_only && !$query['csv_export']) // do NOT store state for csv_export or querying id's (no regular view)
{
$store_query = $query;
// Do not store these
foreach(array('options-cat_id','actions') as $key)
{
unset($store_query[$key]);
}
$old_state = egw_session::appsession($what,'addressbook',$store_query);
}
else
{
$old_state = egw_session::appsession($what,'addressbook');
}
if (!isset($this->org_views[(string) $query['org_view']])) // we dont have an org view, unset the according col_filters
{
if (isset($query['col_filter']['org_name'])) unset($query['col_filter']['org_name']);
if (isset($query['col_filter']['adr_one_locality'])) unset($query['col_filter']['adr_one_locality']);
if (isset($query['col_filter']['org_unit'])) unset($query['col_filter']['org_unit']);
}
if (isset($this->org_views[(string) $query['org_view']])) // we have an org view, reset the advanced search
{
//_debug_array(array('Search'=>$query['search'],
// 'AdvancedSearch'=>$query['advanced_search']));
//if (is_array($query['search'])) unset($query['search']);
//unset($query['advanced_search']);
if(!$query['search'] && $old_state['advanced_search']) $query['advanced_search'] = $old_state['advanced_search'];
}
elseif(!$query['search'] && $old_state['advanced_search']) // eg. paging in an advanced search
{
$query['advanced_search'] = $old_state['advanced_search'];
}
if ($do_email && etemplate::$loop)
{ // remove previous addEmail() calls, otherwise they will be run again
egw_framework::set_onload(preg_replace('/addEmail\([^)]+\);/','',egw_framework::set_onload()),true);
}
// Make sure old lettersearch filter doesn't stay - current letter filter will be added later
foreach($query['col_filter'] as $key => $col_filter)
{
if(!is_numeric($key)) continue;
if(preg_match('/'.$GLOBALS['egw']->db->capabilities['case_insensitive_like'].
' '.$GLOBALS['egw']->db->quote('[a-z]%').'$/',$col_filter) == 1
)
{
unset($query['col_filter'][$key]);
}
}
//echo "<p>uicontacts::get_rows(".print_r($query,true).")</p>\n";
if (!$id_only)
{
// check if accounts are stored in ldap, which does NOT yet support the org-views
if ($this->so_accounts && $query['filter'] === '0' && $query['org_view'])
{
if ($old_state['filter'] === '0') // user changed to org_view
{
$query['filter'] = ''; // --> change filter to all contacts
}
else // user changed to accounts
{
$query['org_view'] = ''; // --> change to regular contacts view
}
}
if ($query['org_view'] && isset($this->org_views[$old_state['org_view']]) && !isset($this->org_views[$query['org_view']]))
{
$query['searchletter'] = ''; // reset lettersearch if viewing the contacts of one organisation
}
// save the state of the index in the user prefs
$state = serialize(array(
'filter' => $query['filter'],
'cat_id' => $query['cat_id'],
'order' => $query['order'],
'sort' => $query['sort'],
'col_filter' => array('tid' => $query['col_filter']['tid']),
'org_view' => $query['org_view'],
));
if ($state != $this->prefs[$what.'_state'] && !$query['csv_export'])
{
$GLOBALS['egw']->preferences->add('addressbook',$what.'_state',$state);
// save prefs, but do NOT invalid the cache (unnecessary)
$GLOBALS['egw']->preferences->save_repository(false,'user',false);
}
}
unset($old_state);
if ((string)$query['cat_id'] != '')
{
$query['col_filter']['cat_id'] = $query['cat_id'] ? $query['cat_id'] : null;
}
else
{
unset($query['col_filter']['cat_id']);
}
if ($query['filter'] !== '') // not all addressbooks
{
$query['col_filter']['owner'] = (string) (int) $query['filter'];
if ($this->private_addressbook)
{
$query['col_filter']['private'] = substr($query['filter'],-1) == 'p' ? 1 : 0;
}
}
else
{
unset($query['col_filter']['owner']);
unset($query['col_filter']['private']);
}
if ((int)$query['filter2']) // not no distribution list
{
$query['col_filter']['list'] = (string) (int) $query['filter2'];
}
else
{
unset($query['col_filter']['list']);
}
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts'])
{
$query['col_filter']['account_id'] = null;
}
// enable/disable distribution lists depending on backend
$query['no_filter2'] = !$this->lists_available($query['filter']);
if (isset($this->org_views[(string) $query['org_view']])) // we have an org view
{
if($query['actions'] && $query['actions']['edit'])
{
// Just switched from contact view, update actions
$query['actions'] = $this->get_actions($query['col_filter']['tid'], $query['org_view']);
}
unset($query['col_filter']['list']); // does not work together
$query['no_filter2'] = true; // switch the distribution list selection off
$query['template'] = 'addressbook.index.org_rows';
if ($query['order'] != 'org_name')
{
$query['sort'] = 'ASC';
$query['order'] = 'org_name';
}
if ($query['advanced_search'])
{
$query['op'] = $query['advanced_search']['operator'];
unset($query['advanced_search']['operator']);
$query['wildcard'] = $query['advanced_search']['meth_select'];
unset($query['advanced_search']['meth_select']);
$original_search = $query['search'];
$query['search'] = $query['advanced_search'];
}
$rows = parent::organisations($query);
if ($query['advanced_search'])
{
$query['search'] = $original_search;
unset($query['wildcard']);
unset($query['op']);
}
$GLOBALS['egw_info']['flags']['params']['manual'] = array('page' => 'ManualAddressbookIndexOrga');
}
else // contacts view
{
if ($query['sitemgr_display'])
{
$query['template'] = $query['sitemgr_display'].'.rows';
}
else
{
$query['template'] = $do_email ? 'addressbook.email.rows' : 'addressbook.index.rows';
}
if ($query['org_view']) // view the contacts of one organisation only
{
if (strpos($query['org_view'],'*AND*') !== false) $query['org_view'] = str_replace('*AND*','&',$query['org_view']);
foreach(explode('|||',$query['org_view']) as $part)
{
list($name,$value) = explode(':',$part,2);
$query['col_filter'][$name] = $value;
}
}
else if($query['actions'] && !$query['actions']['edit'])
{
// Just switched from org view, update actions
$query['actions'] = $this->get_actions($query['col_filter']['tid'], $query['org_view']);
}
// translate the select order to the really used over all 3 columns
$sort = $query['sort'];
switch($query['order']) // "xxx<>'' DESC" sorts contacts with empty order-criteria always at the end
{ // we don't exclude them, as the total would otherwise depend on the order-criteria
case 'org_name':
$order = "egw_addressbook.org_name<>''DESC,egw_addressbook.org_name $sort,n_family $sort,n_given $sort";
break;
default:
if ($query['order'][0] == '#') // we order by a custom field
{
$order = "{$query['order']} $sort,org_name $sort,n_family $sort,n_given $sort";
break;
}
$query['order'] = 'n_family';
case 'n_family':
$order = "n_family<>'' DESC,n_family $sort,n_given $sort,org_name $sort";
break;
case 'n_given':
$order = "n_given<>'' DESC,n_given $sort,n_family $sort,org_name $sort";
break;
case 'n_fileas':
$order = "n_fileas<>'' DESC,n_fileas $sort";
break;
case 'adr_one_postalcode':
case 'adr_two_postalcode':
$order = $query['order']."<>'' DESC,".$query['order']." $sort,org_name $sort,n_family $sort,n_given $sort";
break;
case 'contact_modified':
case 'contact_created':
$order = "$query[order] IS NULL,$query[order] $sort,org_name $sort,n_family $sort,n_given $sort";
break;
case 'contact_id':
$order = "egw_addressbook.$query[order] $sort";
}
if ($query['searchletter']) // only show contacts if the order-criteria starts with the given letter
{
$no_letter_search = array('adr_one_postalcode', 'adr_two_postalcode', 'contact_id', 'contact_created','contact_modified');
$query['col_filter'][] = (in_array($query['order'],$no_letter_search) ? 'org_name' : (substr($query['order'],0,1)=='#'?'':'egw_addressbook.').$query['order']).' '.
$GLOBALS['egw']->db->capabilities['case_insensitive_like'].' '.$GLOBALS['egw']->db->quote($query['searchletter'].'%');
}
$wildcard = '%';
$op = 'OR';
if ($query['advanced_search'])
{
$op = $query['advanced_search']['operator'];
unset($query['advanced_search']['operator']);
$wildcard = $query['advanced_search']['meth_select'];
unset($query['advanced_search']['meth_select']);
}
//if ($do_email ) $email_only = array('id','owner','tid','n_fn','n_family','n_given','org_name','email','email_home');
$rows = parent::search($query['advanced_search'] ? $query['advanced_search'] : $query['search'],$id_only,
$order,'',$wildcard,false,$op,array((int)$query['start'],(int) $query['num_rows']),$query['col_filter']);
// do we need to read the custom fields, depends on the column is enabled and customfields exist
// $query['csv_export'] allways needs to read ALL cf's
$columselection = $this->prefs['nextmatch-addressbook.'.($do_email ? 'email' : 'index').'.rows'];
$available_distib_lists=$this->get_lists(EGW_ACL_READ);
$columselection = $columselection && !$query['csv_export'] ? explode(',',$columselection) : array();
if (!$id_only && $rows)
{
$show_custom_fields = (!$columselection || in_array('customfields',$columselection) || $query['csv_export']) && $this->customfields;
$show_calendar = !$columselection || in_array('calendar_calendar',$columselection);
$show_distributionlist = !$columselection || in_array('distrib_lists',$columselection) || count($available_distib_lists);
if ($show_calendar || $show_custom_fields || $show_distributionlist)
{
foreach($rows as $val)
{
$ids[] = $val['id'];
}
if ($show_custom_fields)
{
foreach($columselection as $col)
{
if ($col[0] == '#') $selected_cfs[] = substr($col,1);
}
$customfields = $this->read_customfields($ids,$selected_cfs);
}
if ($show_calendar && !empty($ids)) $calendar = $this->read_calendar($ids);
// distributionlist memership for the entrys
//_debug_array($this->get_lists(EGW_ACL_EDIT));
if ($show_distributionlist && $available_distib_lists)
{
$distributionlist = $this->read_distributionlist($ids,array_keys($available_distib_lists));
}
}
}
}
if (!$rows) $rows = array();
if ($id_only)
{
foreach($rows as $n => $row)
{
$rows[$n] = $row['id'];
}
return $this->total; // no need to set other fields or $readonlys
}
$order = $query['order'];
$readonlys = array();
$photos = $homeaddress = $roles = $notes = false;
foreach($rows as $n => &$row)
{
$given = $row['n_given'] ? $row['n_given'] : ($row['n_prefix'] ? $row['n_prefix'] : '');
switch($order)
{
default: // postalcode, created, modified, ...
case 'org_name':
$row['line1'] = $row['org_name'];
$row['line2'] = $row['n_family'].($given ? ', '.$given : '');
break;
case 'n_family':
$row['line1'] = $row['n_family'].($given ? ', '.$given : '');
$row['line2'] = $row['org_name'];
break;
case 'n_given':
$row['line1'] = $given.' '.$row['n_family'];
$row['line2'] = $row['org_name'];
break;
case 'n_fileas':
if (!$row['n_fileas']) $row['n_fileas'] = $this->fileas($row);
list($row['line1'],$row['line2']) = explode(': ',$row['n_fileas']);
break;
}
if (isset($this->org_views[(string) $query['org_view']]))
{
$row['type'] = 'home';
$row['type_label'] = lang('Organisation');
if ($query['filter'] && !($this->grants[(int)$query['filter']] & EGW_ACL_DELETE))
{
$row['class'] .= 'rowNoDelete ';
}
$row['class'] .= 'rowNoEdit '; // no edit in OrgView
}
else
{
$this->type_icon($row['owner'],$row['private'],$row['tid'],$row['type'],$row['type_label']);
static $tel2show = array('tel_work','tel_cell','tel_home','tel_fax');
static $prefer_marker;
if (is_null($prefer_marker))
{
// as et2 adds options with .text(), it can't be entities, but php knows no string literals with utf-8
$prefer_marker = html_entity_decode(' &#9829;', ENT_NOQUOTES, 'utf-8');
}
foreach($tel2show as $name)
{
$row[$name] .= ' '.($row['tel_prefer'] == $name ? $prefer_marker : ''); // .' ' to NOT remove the field
}
// allways show the prefered phone, if not already shown
if (!in_array($row['tel_prefer'],$tel2show) && $row[$row['tel_prefer']])
{
$row['tel_prefered'] = $row[$row['tel_prefer']].$prefer_marker;
}
if (!$this->check_perms(EGW_ACL_DELETE,$row) || (!$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge' && $query['col_filter']['tid'] == addressbook_so::DELETED_TYPE))
{
$row['class'] .= 'rowNoDelete ';
}
if (!$this->check_perms(EGW_ACL_EDIT,$row))
{
$row['class'] .= 'rowNoEdit ';
}
unset($row['jpegphoto']); // unused and messes up json encoding (not utf-8)
if ($row['photo']) $photos = true;
if ($row['role']) $roles = true;
if ($row['note']) $notes = true;
if (isset($customfields[$row['id']]))
{
foreach($this->customfields as $name => $data)
{
$row['#'.$name] = $customfields[$row['id']]['#'.$name];
}
}
if (isset($distributionlist[$row['id']]))
{
$row['distrib_lists'] = implode("\n",array_values($distributionlist[$row['id']]));
//if ($show_distributionlist) $readonlys['distrib_lists'] =true;
}
if (isset($calendar[$row['id']]))
{
foreach($calendar[$row['id']] as $name => $data)
{
$row[$name] = $data;
}
}
if ($this->prefs['home_column'] != 'never' && !$homeaddress)
{
foreach(array('adr_two_countryname','adr_two_locality','adr_two_postalcode','adr_two_street','adr_two_street2') as $name)
{
if ($row[$name]) $homeaddress = true;
}
}
}
// hide region for address format 'postcode_city'
if (($row['addr_format'] = $this->addr_format_by_country($row['adr_one_countryname']))=='postcode_city') unset($row['adr_one_region']);
if (($row['addr_format2'] = $this->addr_format_by_country($row['adr_two_countryname']))=='postcode_city') unset($row['adr_two_region']);
// respect category permissions
if(!empty($row['cat_id']))
{
$row['cat_id'] = $this->categories->check_list(EGW_ACL_READ,$row['cat_id']);
}
}
$readonlys['no_distrib_lists'] = (bool)$show_distributionlist;
if (!$this->prefs['no_auto_hide'])
{
// disable photo column, if view contains no photo(s)
if (!$photos) $rows['no_photo'] = true;
// disable homeaddress column, if we have no homeaddress(es)
if (!$homeaddress) $rows['no_home_adr_two_countrycode_adr_two_postalcode'] = true;
// disable roles column
if (!$roles) $rows['no_role'] = true;
// disable note column
if (!$notes) $rows['no_note'] = true;
}
// disable customfields column, if we have no customefield(s)
if (!$this->customfields/* || !$this->prefs['no_auto_hide'] && !$customfields*/) $rows['no_customfields'] = true;
$rows['order'] = $order;
$rows['call_popup'] = $this->config['call_popup'];
$rows['customfields'] = array_values($this->customfields);
// full app-header with all search criteria specially for the print
$GLOBALS['egw_info']['flags']['app_header'] = lang('addressbook');
if ($query['filter'] !== '' && !isset($this->org_views[$query['org_view']]))
{
$GLOBALS['egw_info']['flags']['app_header'] .= ' '.($query['filter'] == '0' ? lang('accounts') :
($GLOBALS['egw']->accounts->get_type($query['filter']) == 'g' ?
lang('Group %1',$GLOBALS['egw']->accounts->id2name($query['filter'])) :
common::grab_owner_name((int)$query['filter']).
(substr($query['filter'],-1) == 'p' ? ' ('.lang('private').')' : '')));
}
if ($query['org_view'])
{
$GLOBALS['egw_info']['flags']['app_header'] .= ': '.$query['org_view_label'];
// Make sure option is there
if(!array_key_exists($query['org_view'], $this->org_views))
{
$this->org_views += $this->_get_org_name($query['org_view']);
$rows['sel_options']['org_view'] = $this->org_views;
}
}
if($query['advanced_search'])
{
$GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Advanced search');
}
if ($query['cat_id'])
{
$GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Category').' '.$GLOBALS['egw']->categories->id2name($query['cat_id']);
}
if ($query['searchletter'])
{
$order = $order == 'n_given' ? lang('first name') : ($order == 'n_family' ? lang('last name') : lang('Organisation'));
$GLOBALS['egw_info']['flags']['app_header'] .= ' - '.lang("%1 starts with '%2'",$order,$query['searchletter']);
}
if ($query['search'] && !$query['advanced_search']) // do not add that, if we have advanced search active
{
$GLOBALS['egw_info']['flags']['app_header'] .= ' - '.lang("Search for '%1'",$query['search']);
}
return $this->total;
}
/**
* Get addressbook type icon from owner, private and tid
*
* @param int $owner user- or group-id or 0 for accounts
* @param boolean $private
* @param string $tid 'n' for regular addressbook
* @param string &$icon icon-name
* @param string &$label translated label
*/
function type_icon($owner,$private,$tid,&$icon,&$label)
{
if (!$owner)
{
$icon = 'accounts';
$label = lang('accounts');
}
elseif ($private)
{
$icon = 'private';
$label = lang('private');
}
elseif ($GLOBALS['egw']->accounts->get_type($owner) == 'g')
{
$icon = 'group';
$label = lang('group %1',$GLOBALS['egw']->accounts->id2name($owner));
}
else
{
$icon = 'personal';
$label = $owner == $this->user ? lang('personal') : common::grab_owner_name($owner);
}
// show tid icon for tid!='n' AND only if one is defined
if ($tid != 'n' && $this->content_types[$tid]['options']['icon'])
{
$icon = $this->content_types[$tid]['options']['icon'];
$label = $this->content_types[$tid]['name'].' ('.$label.')';
}
}
/**
* Edit a contact
*
* @param array $content=null submitted content
* @param int $_GET['contact_id'] contact_id mainly for popup use
* @param bool $_GET['makecp'] true if you want to copy the contact given by $_GET['contact_id']
*/
function edit($content=null)
{
if (is_array($content))
{
list($button) = @each($content['button']);
unset($content['button']);
$content['private'] = (int) ($content['owner'] && substr($content['owner'],-1) == 'p');
$content['owner'] = (string) (int) $content['owner'];
$content['cat_id'] = $this->config['cat_tab'] === 'Tree' ? $content['cat_id_tree'] : $content['cat_id'];
$content += (array)$content['private_cfs'];
unset($content['private_cfs']);
switch($button)
{
case 'save':
case 'apply':
if ($content['delete_photo']) $content['jpegphoto'] = null;
if (is_array($content['upload_photo']) && !empty($content['upload_photo']['tmp_name']) &&
$content['upload_photo']['tmp_name'] != 'none' &&
($f = fopen($content['upload_photo']['tmp_name'],'r')))
{
$content['jpegphoto'] = $this->resize_photo($f);
fclose($f);
unset($content['upload_photo']);
}
$links = false;
if (!$content['id'] && is_array($content['link_to']['to_id']))
{
$links = $content['link_to']['to_id'];
}
$fullname = $old_fullname = parent::fullname($content);
if ($content['id'] && $content['org_name'] && $content['change_org'])
{
$old_org_entry = $this->read($content['id']);
$old_fullname = ($old_org_entry['n_fn'] ? $old_org_entry['n_fn'] : parent::fullname($old_org_entry));
}
if ( $content['n_fn'] != $fullname || $fullname != $old_fullname)
{
unset($content['n_fn']);
}
// Country codes
foreach(array('adr_one', 'adr_two') as $c_prefix)
{
if ($content[$c_prefix.'_countrycode'] == '-custom-')
{
$content[$c_prefix.'_countrycode'] = null;
}
}
if ($this->save($content))
{
$content['msg'] = lang('Contact saved');
if ($content['change_org'] && $old_org_entry && ($changed = $this->changed_fields($old_org_entry,$content,true)) &&
($members = $this->org_similar($old_org_entry['org_name'],$changed)))
{
//foreach($changed as $name => $old_value) echo "<p>$name: '$old_value' --> '{$content[$name]}'</p>\n";
list($changed_members,$changed_fields,$failed_members) = $this->change_org($old_org_entry['org_name'],$changed,$content,$members);
if ($changed_members)
{
$content['msg'] .= ', '.lang('%1 fields in %2 other organisation member(s) changed',$changed_fields,$changed_members);
}
if ($failed_members)
{
$content['msg'] .= ', '.lang('failed to change %1 organisation member(s) (insufficent rights) !!!',$failed_members);
}
}
}
elseif($this->error === true)
{
$content['msg'] = lang('Error: the entry has been updated since you opened it for editing!').'<br />'.
lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'.
htmlspecialchars(egw::link('/index.php',array(
'menuaction' => 'addressbook.addressbook_ui.edit',
'contact_id' => $content['id'],
))).'">','</a>');
break; // dont refresh the list
}
else
{
$content['msg'] = lang('Error saving the contact !!!').
($this->error ? ' '.$this->error : '');
$button = 'apply'; // to not leave the dialog
}
// writing links for new entry, existing ones are handled by the widget itself
if ($links && $content['id'])
{
egw_link::link('addressbook',$content['id'],$links);
}
$currentApp = $GLOBALS['egw']->currentapp;
error_log(__METHOD__. "() currentapp:" . $currentApp);
egw_framework::refresh_opener($content['msg'], 'addressbook',$content['id'], ($content['id'] ? 'update' : 'add'));
if ($button == 'save')
{
egw_framework::window_close();
}
$content['link_to']['to_id'] = $content['id'];
break;
case 'delete':
if($this->action('delete',array($content['id']),false,$success,$failed,$action_msg,'',$content['msg']))
{
if ($GLOBALS['egw']->currentapp == 'addressbook')
{
egw_framework::refresh_opener(lang('Contact deleted'), 'addressbook', $content['id'], 'delete' );
egw_framework::window_close();
}
else
{
egw_framework::refresh_opener(lang('Contact deleted'), 'addressbook', $content['id'], null, 'addressbook');
egw_framework::window_close();
}
}
else
{
$content['msg'] = lang('Error deleting the contact !!!');
}
break;
}
// type change
}
else
{
$content = array();
$contact_id = $_GET['contact_id'] ? $_GET['contact_id'] : ((int)$_GET['account_id'] ? 'account:'.(int)$_GET['account_id'] : 0);
$view = $_GET['view'];
// new contact --> set some defaults
if ($contact_id && is_array($content = $this->read($contact_id)))
{
$contact_id = $content['id']; // it could have been: "account:$account_id"
}
else // not found
{
$state = egw_session::appsession('index','addressbook');
// check if we create the new contact in an existing org
if (($org = $_GET['org']))
{
// arguments containing a comma get quoted by etemplate/js/nextmatch_action.js
// leading to error in egw_db::column_data_implode, if not unquoted
if ($org[0] == '"') $org = substr($org, 1, -1);
$content = $this->read_org($org);
}
elseif ($state['org_view'] && !isset($this->org_views[$state['org_view']]))
{
$content = $this->read_org($state['org_view']);
}
else
{
if ($GLOBALS['egw_info']['user']['preferences']['common']['country'])
{
$content['adr_one_countrycode'] =
$GLOBALS['egw_info']['user']['preferences']['common']['country'];
$content['adr_one_countryname'] =
$GLOBALS['egw']->country->get_full_name($GLOBALS['egw_info']['user']['preferences']['common']['country']);
$content['adr_two_countrycode'] =
$GLOBALS['egw_info']['user']['preferences']['common']['country'];
$content['adr_two_countryname'] =
$GLOBALS['egw']->country->get_full_name($GLOBALS['egw_info']['user']['preferences']['common']['country']);
}
if ($this->prefs['fileas_default']) $content['fileas_type'] = $this->prefs['fileas_default'];
}
if (isset($_GET['owner']) && $_GET['owner'] !== '')
{
$content['owner'] = $_GET['owner'];
}
else
{
$content['owner'] = $state['filter'];
}
$content['private'] = (int) ($content['owner'] && substr($content['owner'],-1) == 'p');
if (!($this->grants[$content['owner'] = (string) (int) $content['owner']] & EGW_ACL_ADD))
{
$content['owner'] = $this->default_addressbook;
$content['private'] = (int)$this->default_private;
if (!($this->grants[$content['owner'] = (string) (int) $content['owner']] & EGW_ACL_ADD))
{
$content['owner'] = (string) $this->user;
$content['private'] = 0;
}
}
$new_type = array_keys($this->content_types);
// fetch active type to preset the type, if param typeid is not passed
$active_tid = egw_cache::getSession('addressbook','active_tid');
if ($active_tid && strtoupper($active_tid) === 'D') unset($active_tid);
$content['tid'] = $_GET['typeid'] ? $_GET['typeid'] : ($active_tid?$active_tid:$new_type[0]);
foreach($this->get_contact_columns() as $field)
{
if ($_GET['presets'][$field]) $content[$field] = $_GET['presets'][$field];
}
if (isset($_GET['presets']))
{
foreach(array('email','email_home','n_family','n_given','org_name') as $field)
{
if (!empty($content[$field]))
{
egw_framework::set_onload("app.addressbook.check_value(document.getElementById('exec[$field]'),0);");
break;
}
}
}
$content['creator'] = $this->user;
$content['created'] = $this->now_su;
unset($state);
}
if ($_GET['msg']) $content['msg'] = strip_tags($_GET['msg']); // dont allow HTML!
if($content && $_GET['makecp']) // copy the contact
{
$content['link_to']['to_id'] = 0;
egw_link::link('addressbook',$content['link_to']['to_id'],'addressbook',$content['id'],
lang('Copied by %1, from record #%2.',common::display_fullname('',
$GLOBALS['egw_info']['user']['account_firstname'],$GLOBALS['egw_info']['user']['account_lastname']),
$content['id']));
// create a new contact with the content of the old
foreach($content as $key => $value)
{
if(!in_array($key, self::$copy_fields) || in_array($key, array('etag','carddav_name','uid')))
{
unset($content[$key]);
}
}
if(!isset($content['owner']))
{
$content['owner'] = $this->default_private ? $this->user.'p' : $this->default_addressbook;
}
$content['creator'] = $this->user;
$content['created'] = $this->now_su;
$content['msg'] = lang('Contact copied');
}
else
{
if (is_numeric($contact_id)) $content['link_to']['to_id'] = $contact_id;
}
// automatic link new entries to entries specified in the url
if (!$contact_id && isset($_REQUEST['link_app']) && isset($_REQUEST['link_id']) && !is_array($content['link_to']['to_id']))
{
$link_ids = is_array($_REQUEST['link_id']) ? $_REQUEST['link_id'] : array($_REQUEST['link_id']);
foreach(is_array($_REQUEST['link_app']) ? $_REQUEST['link_app'] : array($_REQUEST['link_app']) as $n => $link_app)
{
$link_id = $link_ids[$n];
if (preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$link_app.':'.$link_id)) // gard against XSS
{
egw_link::link('addressbook',$content['link_to']['to_id'],$link_app,$link_id);
}
}
}
}
if ($content['id'])
{
// last and next calendar date
list(,$dates) = each($this->read_calendar(array($content['id']),false));
if(is_array($dates)) $content += $dates;
}
// Avoid ID conflict with tree & selectboxes
$content['cat_id_tree'] = $content['cat_id'];
// Avoid setting conflicts with private custom fields
$content['private_cfs'] = $content;
// how to display addresses
$content['addr_format'] = $this->addr_format_by_country($content['adr_one_countryname']);
$content['addr_format2'] = $this->addr_format_by_country($content['adr_two_countryname']);
//_debug_array($content);
$readonlys['button[delete]'] = !$content['owner'] || !$this->check_perms(EGW_ACL_DELETE,$content);
$readonlys['button[copy]'] = $readonlys['button[edit]'] = $readonlys['button[vcard]'] = true;
$sel_options['fileas_type'] = $this->fileas_options($content);
$sel_options['owner'] = $this->get_addressbooks(EGW_ACL_ADD);
if ((string) $content['owner'] !== '')
{
if (!isset($sel_options['owner'][(int)$content['owner']]))
{
$sel_options['owner'][(int)$content['owner']] = !$content['owner'] ? lang('Accounts') :
common::grab_owner_name($content['owner']);
}
$readonlys['owner'] = !$content['owner'] || // dont allow to move accounts, as this mean deleting the user incl. all content he owns
$content['id'] && !$this->check_perms(EGW_ACL_DELETE,$content); // you need delete rights to move an existing contact into an other addressbook
}
// set the unsupported fields from the backend to readonly
foreach($this->get_fields('unsupported',$content['id'],$content['owner']) as $field)
{
$readonlys[$field] = true;
}
if ($readonlys['n_fileas']) $readonlys['fileas_type'] = true;
// disable not needed tabs
$readonlys['tabs']['cats'] = !($content['cat_tab'] = $this->config['cat_tab']);
$readonlys['tabs']['custom'] = !$this->customfields || $this->get_backend($content['id'],$content['owner']) == $this->so_accounts;
$readonlys['tabs']['custom_private'] = $readonlys['tabs']['custom'] || !$this->config['private_cf_tab'];
$readonlys['tabs']['distribution_list'] = !$content['distrib_lists'];#false;
$readonlys['tabs']['history'] = $this->contact_repository != 'sql' || !$content['id'] ||
$this->account_repository != 'sql' && $content['account_id'];
if (!$content['id']) $readonlys['button[delete]'] = !$content['id'];
if ($this->config['private_cf_tab']) $content['no_private_cfs'] = 0;
$readonlys['change_org'] = empty($content['org_name']) || $view;
// for editing the own account (by a non-admin), enable only the fields allowed via the "own_account_acl"
if (!$content['owner'] && !$this->check_perms(EGW_ACL_EDIT, $content))
{
$this->_set_readonlys_for_own_account_acl($readonlys,$id);
}
for($i = -23; $i<=23; $i++) $tz[$i] = ($i > 0 ? '+' : '').$i;
$sel_options['tz'] = $tz;
$content['tz'] = $content['tz'] ? $content['tz'] : '0';
if (count($this->content_types) > 1)
{
foreach($this->content_types as $type => $data)
{
$sel_options['tid'][$type] = $data['name'];
}
$content['typegfx'] = html::image('addressbook',$this->content_types[$content['tid']]['options']['icon'],'',' width="16px" height="16px"');
}
else
{
$content['no_tid'] = true;
}
$content['view'] = false;
$content['link_to'] = array(
'to_app' => 'addressbook',
'to_id' => $content['link_to']['to_id'],
);
// Links for deleted entries
if($content['tid'] == addressbook_so::DELETED_TYPE)
{
$content['link_to']['show_deleted'] = true;
if(!$GLOBALS['egw_info']['user']['apps']['admin'] && $this->config['history'] != 'userpurge')
{
$readonlys['button[delete]'] = true;
}
}
// Enable history
$this->setup_history($content, $sel_options);
$content['photo'] = $this->photo_src($content['id'],$content['jpegphoto'],'photo',$content['etag']);
if ($content['private']) $content['owner'] .= 'p';
//$GLOBALS['egw_info']['flags']['include_xajax'] = true;
if (!$this->tmpl->read($this->content_types[$content['tid']]['options']['template'] ? $this->content_types[$content['tid']]['options']['template'] : 'addressbook.edit'))
{
$content['msg'] = lang('WARNING: Template "%1" not found, using default template instead.', $this->content_types[$content['tid']]['options']['template'])."\n";
$content['msg'] .= lang('Please update the templatename in your customfields section!');
$this->tmpl->read('addressbook.edit');
}
return $this->tmpl->exec('addressbook.addressbook_ui.edit',$content,$sel_options,$readonlys,$content, 2);
}
/**
* Set the readonlys for non-admins editing their own account
*
* @param array &$readonlys
* @param int $id
*/
function _set_readonlys_for_own_account_acl(&$readonlys,$id)
{
// regular fields depending on the backend
foreach($this->get_fields('supported',$id,0) as $field)
{
if (!$this->own_account_acl || !in_array($field,$this->own_account_acl))
{
$readonlys[$field] = true;
switch($field)
{
case 'tel_work':
case 'tel_cell':
case 'tel_home':
$readonlys[$field.'2'] = true;
break;
case 'n_fileas':
$readonlys['fileas_type'] = true;
break;
}
}
}
// custom fields
if ($this->customfields)
{
foreach($this->customfields as $name => $data)
{
if (!$this->own_account_acl || !in_array('#'.$name,$this->own_account_acl))
{
$readonlys['#'.$name] = true;
}
}
}
// links
if (!$this->own_account_acl || !in_array('link_to',$this->own_account_acl))
{
$readonlys['link_to'] = true;
}
}
/**
* Doublicate check: returns similar contacts: same email or 2 of name, firstname, org
*
* Also update/return fileas options, if necessary.
*
* @param array $values contact values from form
* @param string $name name of changed value, eg. "email"
* @param int $own_id=0 own contact id, to not check against it
* @return array with keys 'msg' => "EMail address exists, do you want to open contact?" (or null if not existing)
* 'data' => array of id => "full name (addressbook)" pairs
* 'fileas_options'
*/
public function ajax_check_values($values, $name, $own_id=0)
{
if (preg_match('/^exec\[([^\]]+)\]$/', $name, $matches)) $name = $matches[1]; // remove exec[ ]
$ret = array('doublicates' => array(), 'msg' => null);
// if email changed, check for doublicates
if (in_array($name, array('email', 'email_home')))
{
if (preg_match('/^'.url_widget::EMAIL_PREG.'$/i', $values[$name])) // only search for real email addresses, to not return to many contacts
{
$contacts = parent::search(array(
'email' => $values[$name],
'email_home' => $values[$name],
),$only_keys=false, $order_by='', $extra_cols='', $wildcard='', $empty=False, $op='OR');
}
}
else
{
// only set fileas-options if other then email changed
$ret['fileas_options'] = array_values($this->fileas_options($values));
// Full options for et2
$ret['fileas_sel_options'] = $this->fileas_options($values);
// if name, firstname or org changed and at least 2 are specified, check for doublicates
if (in_array($name, array('n_given', 'n_family', 'org_name')) &&
!empty($values['n_given'])+!empty($values['n_family'])+!empty($values['org_name']) >= 2)
{
$filter = array();
foreach(array('email', 'n_given', 'n_family', 'org_name') as $n) // use email too, to exclude obvious false positives
{
if (!empty($values[$n])) $filter[$n] = $values[$n];
}
$contacts = parent::search($criteria='', $only_keys=false, $order_by='', $extra_cols='', $wildcard='',
$empty=False, $op='AND', $start=false, $filter);
}
}
if ($contacts)
{
foreach($contacts as $contact)
{
if ($own_id && $contact['id'] == $own_id) continue;
$ret['doublicates'][$contact['id']] = $this->fileas($contact).' ('.
(!$contact['owner'] ? lang('Accounts') : ($contact['owner'] == $this->user ?
($contact['private'] ? lang('Private') : lang('Personal')) : common::grab_owner_name($contact['owner']))).')';
}
if ($ret['doublicates'])
{
$ret['msg'] = lang('Similar contacts found:').
"\n\n".implode("\n", $ret['doublicates'])."\n\n".
lang('Open for editing?');
}
}
//error_log(__METHOD__.'('.array2string($values).", '$name', $own_id) doublicates found ".array2string($ret['doublicates']));
egw_json_response::get()->data($ret);
}
/**
* CRM view
*
* @param array $content
*/
function view(array $content=null)
{
if(is_array($content))
{
list($button) = each($content['button']);
switch ($content['toolbar'] ? $content['toolbar'] : $button)
{
case 'vcard':
egw::redirect_link('/index.php','menuaction=addressbook.uivcard.out&ab_id=' .$content['id']);
case 'cancel':
egw::redirect_link('/index.php','menuaction=addressbook.addressbook_ui.index&ajax=true');
case 'delete':
egw::redirect_link('/index.php',array(
'menuaction' => 'addressbook.addressbook_ui.index',
'msg' => $this->delete($content) ? lang('Contact deleted') : lang('Error deleting the contact !!!'),
));
}
}
else
{
$contact_id = $_GET['contact_id'] ? $_GET['contact_id'] : ((int)$_GET['account_id'] ? 'account:'.(int)$_GET['account_id'] : 0);
if(!$contact_id || !is_array($content = $this->read($contact_id)))
{
egw::redirect_link('/index.php',array(
'menuaction' => 'addressbook.addressbook_ui.index',
'msg' => $content,
'ajax' => 'true'
));
}
}
// make everything not explicit mentioned readonly
$readonlys['__ALL__'] = true;
$readonlys['photo'] = $readonlys['button[cancel]'] = $readonlys['button[copy]'] =
$readonlys['button[ok]'] = $readonlys['button[more]'] = $readonlys['toolbar'] = false;
foreach(array_keys($this->contact_fields) as $key)
{
if (in_array($key,array('tel_home','tel_work','tel_cell','tel_fax')))
{
$content[$key.'2'] = $content[$key];
}
}
// respect category permissions
if(!empty($content['cat_id']))
{
$content['cat_id'] = $this->categories->check_list(EGW_ACL_READ,$content['cat_id']);
}
$content['cat_id_tree'] = $content['cat_id'];
$content['view'] = true;
$content['link_to'] = array(
'to_app' => 'addressbook',
'to_id' => $content['id'],
);
// Links for deleted entries
if($content['tid'] == addressbook_so::DELETED_TYPE)
{
$content['link_to']['show_deleted'] = true;
}
$readonlys['button[delete]'] = !$content['owner'] || !$this->check_perms(EGW_ACL_DELETE,$content);
$readonlys['button[edit]'] = !$this->check_perms(EGW_ACL_EDIT,$content);
// how to display addresses
$content['addr_format'] = $this->addr_format_by_country($content['adr_one_countryname']);
$content['addr_format2'] = $this->addr_format_by_country($content['adr_two_countryname']);
$sel_options['fileas_type'][$content['fileas_type']] = $this->fileas($content);
$sel_options['owner'] = $this->get_addressbooks();
for($i = -23; $i<=23; $i++) $tz[$i] = ($i > 0 ? '+' : '').$i;
$sel_options['tz'] = $tz;
$content['tz'] = $content['tz'] ? $content['tz'] : 0;
if (count($this->content_types) > 1)
{
foreach($this->content_types as $type => $data)
{
$sel_options['tid'][$type] = $data['name'];
}
$content['typegfx'] = html::image('addressbook',$this->content_types[$content['tid']]['options']['icon'],'',' width="16px" height="16px"');
}
else
{
$content['no_tid'] = true;
}
$this->tmpl->read('addressbook.view');
/*if (!$this->tmpl->read($this->content_types[$content['tid']]['options']['template'] ? $this->content_types[$content['tid']]['options']['template'] : 'addressbook.edit'))
{
$content['msg'] = lang('WARNING: Template "%1" not found, using default template instead.', $this->content_types[$content['tid']]['options']['template'])."\n";
$content['msg'] .= lang('Please update the templatename in your customfields section!');
$this->tmpl->read('addressbook.edit');
}*/
if ($this->private_addressbook && $content['private'] && $content['owner'] == $this->user)
{
$content['owner'] .= 'p';
}
$this->tmpl->set_cell_attribute('change_org','disabled',true);
// Prevent double countries - invalid code blanks it, disabling doesn't work
$content['adr_one_countrycode'] = '-';
$content['adr_two_countrycode'] = '-';
// Enable history
$this->setup_history($content, $sel_options);
// disable not needed tabs
$readonlys['tabs']['cats'] = !($content['cat_tab'] = $this->config['cat_tab']);
$readonlys['tabs']['custom'] = !$this->customfields;
$readonlys['tabs']['custom_private'] = !$this->customfields || !$this->config['private_cf_tab'];
$readonlys['tabs']['distribution_list'] = !$content['distrib_lists'];#false;
$readonlys['tabs']['history'] = $this->contact_repository != 'sql' || !$content['id'] ||
$this->account_repository != 'sql' && $content['account_id'];
if ($this->config['private_cf_tab']) $content['no_private_cfs'] = 0;
// last and next calendar date
if (!empty($content['id'])) list(,$dates) = each($this->read_calendar(array($content['id']),false));
if(is_array($dates)) $content += $dates;
// Disable importexport
$GLOBALS['egw_info']['flags']['disable_importexport']['export'] = true;
$GLOBALS['egw_info']['flags']['disable_importexport']['merge'] = true;
// set id for automatic linking via quick add
$GLOBALS['egw_info']['flags']['currentid'] = $content['id'];
// load app.css for addressbook explicit, as addressbook_view hooks changes currentapp!
egw_framework::includeCSS('addressbook', 'app');
// dont show an app-header
$GLOBALS['egw_info']['flags']['app_header'] = '';
$this->tmpl->setElementAttribute('toolbar', 'actions', array(
'edit' => array(
'caption' => 'Edit',
'toolbarDefault' => true,
),
'copy' => 'Copy',
'delete' => array(
'caption' => 'Delete',
'confirm' => 'Delete this entry',
),
'cancel' => array(
'caption' => 'Cancel',
'toolbarDefault' => true,
),
'back' => array(
'caption' => 'Back',
'toolbarDefault' => true,
),
'next' => array(
'caption' => 'Next',
'toolbarDefault' => true,
),
));
// always show sidebox, as it contains contact-data
unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
// need to load infolog's app.js now, as it exec calls header before infolog can include it
egw_framework::validate_file('/infolog/js/app.js');
$this->tmpl->exec('addressbook.addressbook_ui.view',$content,$sel_options,$readonlys,array('id' => $content['id']));
// Only load this on first time - we're using AJAX, so it stays there through submits.
// Sending it again (via ajax) will break the addressbook.view etemplate2
if($contact_id)
{
$GLOBALS['egw']->hooks->process(array(
'location' => 'addressbook_view',
'ab_id' => $content['id']
));
}
}
/**
* convert email-address in compose link
*
* @param string $email email-addresse
* @return array/string array with get-params or mailto:$email, or '' or no mail addresse
*/
function email2link($email)
{
if (strpos($email,'@') == false) return '';
if($GLOBALS['egw_info']['user']['apps']['felamimail'])
{
return array(
'menuaction' => 'felamimail.uicompose.compose',
'send_to' => base64_encode($email)
);
}
if($GLOBALS['egw_info']['user']['apps']['email'])
{
return array(
'menuaction' => 'email.uicompose.compose',
'to' => $email,
);
}
return 'mailto:' . $email;
}
/**
* Extended search
*
* @param array $_content
* @return string
*/
function search($_content=array())
{
if(!empty($_content))
{
$response = egw_json_response::get();
$query = egw_session::appsession('index','addressbook');
if ($_content['button']['cancelsearch'])
{
unset($query['advanced_search']);
}
else
{
$query['advanced_search'] = array_intersect_key($_content,array_flip(array_merge($this->get_contact_columns(),array('operator','meth_select'))));
foreach ($query['advanced_search'] as $key => $value)
{
if(!$value) unset($query['advanced_search'][$key]);
}
}
$query['start'] = 0;
$query['search'] = '';
// store the index state in the session
egw_session::appsession('index','addressbook',$query);
// store the advanced search in the session to call it again
egw_session::appsession('advanced_search','addressbook',$query['advanced_search']);
error_log(__METHOD__. "() call ADV" );
if ($_content['button']['search']) $response->call("app.addressbook.adv_search");
if ($_content['button']['cancelsearch']) egw_framework::window_close (); //$response->addScript('this.close();');
}
//$GLOBALS['egw_info']['flags']['include_xajax'] = true;
//$GLOBALS['egw_info']['flags']['java_script'] .= "<script>window.egw_LAB.wait(function() {window.focus();});</script>";
$GLOBALS['egw_info']['etemplate']['advanced_search'] = true;
// initialize etemplate arrays
$sel_options = $readonlys = array();
$content = egw_session::appsession('advanced_search','addressbook');
$content['n_fn'] = $this->fullname($content);
for($i = -23; $i<=23; $i++) $tz[$i] = ($i > 0 ? '+' : '').$i;
$sel_options['tz'] = $tz + array('' => lang('doesn\'t matter'));
$sel_options['tid'][] = lang('all');
//foreach($this->content_types as $type => $data) $sel_options['tid'][$type] = $data['name'];
// configure search options
$sel_options['owner'] = $this->get_addressbooks(EGW_ACL_READ,lang('all'));
$sel_options['operator'] = array(
'AND' => 'AND',
'OR' => 'OR',
);
$sel_options['meth_select'] = array(
'%' => lang('contains'),
false => lang('exact'),
);
if ($this->customfields)
{
foreach($this->customfields as $name => $data)
{
if ($data['type'] == 'select')
{
if (!isset($content['#'.$name])) $content['#'.$name] = '';
if(!isset($data['values'][''])) $sel_options['#'.$name][''] = lang('Select one');
}
}
}
// configure edit template as search dialog
$readonlys['change_photo'] = true;
$readonlys['fileas_type'] = true;
$readonlys['creator'] = true;
// this setting will enable (and show) the search and cancel buttons, setting this to true will hide the before mentioned buttons completely
$readonlys['button'] = false;
// disable not needed tabs
$readonlys['tabs']['cats'] = !($content['cat_tab'] = $this->config['cat_tab']);
$readonlys['tabs']['custom'] = !$this->customfields;
$readonlys['tabs']['custom_private'] = !$this->customfields || !$this->config['private_cf_tab'];
$readonlys['tabs']['links'] = true;
$readonlys['tabs']['distribution_list'] = true;
$readonlys['tabs']['history'] = true;
// setting hidebuttons for content will hide the 'normal' addressbook edit dialog buttons
$content['hidebuttons'] = true;
$content['no_tid'] = true;
$content['showsearchbuttons'] = true; // enable search operation and search buttons| they're disabled by default
if ($this->config['private_cf_tab']) $content['no_private_cfs'] = 0;
$this->tmpl->read('addressbook.edit');
$this->tmpl->set_cell_attribute('change_org','disabled',true);
return $this->tmpl->exec('addressbook.addressbook_ui.search',$content,$sel_options,$readonlys,array(),2);
}
/**
* download photo of the given ($_GET['contact_id'] or $_GET['account_id']) contact
*/
function photo()
{
ob_start();
$contact_id = isset($_GET['contact_id']) ? $_GET['contact_id'] :
(isset($_GET['account_id']) ? 'account:'.$_GET['account_id'] : 0);
if (substr($contact_id,0,8) == 'account:')
{
$contact_id = $GLOBALS['egw']->accounts->id2name(substr($contact_id,8),'person_id');
}
if (!($contact = $this->read($contact_id)) || !$contact['jpegphoto'])
{
egw::redirect(common::image('addressbook','photo'));
}
// use an etag over the image mapp
$etag = '"'.$contact['id'].':'.$contact['etag'].'"';
if (!ob_get_contents())
{
header('Content-type: image/jpeg');
header('ETag: '.$etag);
// if etag parameter given in url, we can allow browser to cache picture via an Expires header
// different url with different etag parameter will force a reload
if (isset($_GET['etag']))
{
egw_session::cache_control(30*86400); // cache for 30 days
}
// if servers send a If-None-Match header, response with 304 Not Modified, if etag matches
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag)
{
header("HTTP/1.1 304 Not Modified");
}
else
{
header('Content-length: '.bytes($contact['jpegphoto']));
echo $contact['jpegphoto'];
}
common::egw_exit();
}
}
/**
* Migrate contacts to or from LDAP (called by Admin >> Addressbook >> Site configuration (Admin only)
*
*/
function migrate2ldap()
{
$GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Migration to LDAP');
common::egw_header();
parse_navbar();
if (!$this->is_admin())
{
echo '<h1>'.lang('Permission denied !!!')."</h1>\n";
}
else
{
parent::migrate2ldap($_GET['type']);
echo '<p style="margin-top: 20px;"><b>'.lang('Migration finished')."</b></p>\n";
}
common::egw_footer();
}
/**
* Set n_fileas (and n_fn) in contacts of all users (called by Admin >> Addressbook >> Site configuration (Admin only)
*
* If $_GET[all] all fileas fields will be set, if !$_GET[all] only empty ones
*
*/
function admin_set_fileas()
{
translation::add_app('admin');
$GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Contact maintenance');
common::egw_header();
parse_navbar();
// check if user has admin rights AND if a valid fileas type is given (Security)
if (!$this->is_admin() || $_GET['type'] != '' && !in_array($_GET['type'],$this->fileas_types))
{
echo '<h1>'.lang('Permission denied !!!')."</h1>\n";
}
else
{
$updated = parent::set_all_fileas($_GET['type'],(boolean)$_GET['all'],$errors,true); // true = ignore acl
echo '<p style="margin-top: 20px;"><b>'.lang('%1 contacts updated (%2 errors).',$updated,$errors)."</b></p>\n";
}
common::egw_footer();
}
/**
* Cleanup all contacts of all users (called by Admin >> Addressbook >> Site configuration (Admin only)
*
*/
function admin_set_all_cleanup()
{
translation::add_app('admin');
$GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Contact maintenance');
common::egw_header();
parse_navbar();
// check if user has admin rights (Security)
if (!$this->is_admin())
{
echo '<h1>'.lang('Permission denied !!!')."</h1>\n";
}
else
{
$updated = parent::set_all_cleanup($errors,true); // true = ignore acl
echo '<p style="margin-top: 20px;"><b>'.lang('%1 contacts updated (%2 errors).',$updated,$errors)."</b></p>\n";
}
common::egw_footer();
}
/**
* Set up history log widget
*/
protected function setup_history(&$content, &$sel_options)
{
if ($this->contact_repository == 'ldap' || !$content['id'] ||
$this->account_repository == 'ldap' && $content['account_id'])
{
return; // no history for ldap as history table only allows integer id's
}
$content['history'] = array(
'id' => $content['id'],
'app' => 'addressbook',
'status-widgets' => array(
'owner' => 'select-account',
'creator' => 'select-account',
'created' => 'date-time',
'cat_id' => 'select-cat',
'adr_one_countrycode' => 'select-country',
'adr_two_countrycode' => 'select-country',
),
);
foreach($this->content_types as $id => $settings)
{
$content['history']['status-widgets']['tid'][$id] = $settings['name'];
}
$sel_options['status'] = $this->contact_fields;
// custom fields no longer need to be added, historylog-widget "knows" about them
}
}