diff --git a/addressbook/inc/class.addressbook_ui.inc.php b/addressbook/inc/class.addressbook_ui.inc.php index c97317437d..5317d5a6cd 100644 --- a/addressbook/inc/class.addressbook_ui.inc.php +++ b/addressbook/inc/class.addressbook_ui.inc.php @@ -2046,7 +2046,7 @@ class addressbook_ui extends addressbook_bo { $row[$name] .= ' '.($row['tel_prefer'] == $name ? $prefer_marker : ''); // .' ' to NOT remove the field } - // allways show the prefered phone, if not already shown + // always 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; @@ -2070,6 +2070,12 @@ class addressbook_ui extends addressbook_bo } $row['class'] .= 'contact_contact '; + if (!self::hasPhoto($row)) + { + $row['lname'] = $row['n_family']; + $row['fname'] = $row['n_given']; + unset($row['photo']); // no need to send, as there is no photo + } unset($row['jpegphoto']); // unused and messes up json encoding (not utf-8) if (isset($customfields[$row['id']])) diff --git a/api/src/Accounts.php b/api/src/Accounts.php index fe08e2f845..f0b0b8e7b6 100644 --- a/api/src/Accounts.php +++ b/api/src/Accounts.php @@ -446,15 +446,15 @@ class Accounts 'lname' => $account['account_id'] < 0 ? $account['account_lid'] : $account['account_lastname'], 'fname' => $account['account_id'] < 0 ? lang('group') : $account['account_firstname'] ]; - // TODO: Ralf find a cheap way to get this - if($actual_picture) + // only if we have a real photo, send avatar-url, otherwise we use the above set lavatar (f|l)name + if(!empty($account['account_has_photo'])) { $result['icon'] = Framework::link('/api/avatar.php', [ 'account_id' => $account['account_id'], 'modified' => $account['account_modified'], ]); } - $accounts[] = $result; + $accounts[$account['account_id']] = $result; } else { @@ -516,7 +516,7 @@ class Accounts function json($id) { static $keys = array( - 'account_id','account_lid','person_id','account_status','memberships', + 'account_id','account_lid','person_id','account_status','memberships','account_has_photo', 'account_firstname','account_lastname','account_email','account_fullname','account_phone', ); if (($account = $this->read($id))) diff --git a/api/src/Accounts/Sql.php b/api/src/Accounts/Sql.php index 25e999439a..845079040a 100644 --- a/api/src/Accounts/Sql.php +++ b/api/src/Accounts/Sql.php @@ -501,7 +501,7 @@ class Sql $filter[] = "(egw_addressbook.contact_owner=0 OR egw_addressbook.contact_owner IS NULL)"; break; } - // fix ambigous account_id (used in accounts and contacts table) + // fix ambiguous account_id (used in accounts and contacts table) if (array_key_exists('account_id', $filter)) { if (!$filter['account_id']) // eg. group without members (would give SQL error) @@ -561,13 +561,13 @@ class Sql if (!isset($this->contacts)) $this->contacts = new Api\Contacts(); $accounts = array(); - foreach((array) $this->contacts->search($criteria, - array_merge(array(1,'n_given','n_family','id','created','modified',$this->table.'.account_id AS account_id'),$email_cols), + foreach($this->contacts->search($criteria, + array_merge(array(1,'n_given','n_family','id','created','modified','files',$this->table.'.account_id AS account_id'),$email_cols), $order, "account_lid,account_type,account_status,account_expires,account_primary_group,account_description". ",account_lastlogin,account_lastloginfrom,account_lastpwd_change", $wildcard,false,$query[0] == '!' ? 'AND' : 'OR', !empty($param['offset']) ? array($param['start'], $param['offset']) : $param['start'] ?? false, - $filter,$join) as $contact) + $filter,$join) ?? [] as $contact) { if ($contact) { @@ -592,6 +592,7 @@ class Sql 'account_lastpwd_change' => $contact['account_lastpwd_change'] ? Api\DateTime::user2server($contact['account_lastpwd_change']) : null, 'account_description' => $contact['account_description'], + 'account_has_photo' => Api\Contacts::hasPhoto($contact), ); } } diff --git a/api/src/Contacts.php b/api/src/Contacts.php index 998127a959..62eff1ddec 100755 --- a/api/src/Contacts.php +++ b/api/src/Contacts.php @@ -1701,15 +1701,35 @@ class Contacts extends Contacts\Storage $args[] = $result[$contact['id']]; $result[$contact['id']] = call_user_func_array('imap_rfc822_write_address', $args); } + if (!is_array($result[$contact['id']])) + { + $result[$contact['id']] = ['label' => $result[$contact['id']]]; + } + // if we have a real photo, add avatar.php URL + if (!empty($contact['jpegphoto']) || ($contact['files'] & self::FILES_BIT_PHOTO) && + filesize($url= Api\Link::vfs_path('addressbook', $contact['id'], self::FILES_PHOTO))) + { + $result[$contact['id']] += [ + 'icon' => Framework::link('/api/avatar.php', [ + 'contact_id' => $contact['id'], + 'modified' => $contact['modified'], + ]) + ]; + } + // else add Lavatar information lname and fname + else + { + $result[$contact['id']] += [ + 'lname' => $contact['n_family'], + 'fname' => $contact['n_given'], + ]; + } // show category color if ($contact['cat_id'] && ($color = Categories::cats2color($contact['cat_id']))) { - $result[$contact['id']] = array( - 'lname' => $contact['n_family'], - 'fname' => $contact['n_given'], - 'label' => $result[$contact['id']], + $result[$contact['id']] += [ 'style.backgroundColor' => $color, - ); + ]; } } } @@ -1854,14 +1874,14 @@ class Contacts extends Contacts\Storage 'app' => 'calendar', 'title' => $bocal->link_title($cal_id . ($start ? '-'.$start : '')), 'extra_args' => array( - 'date' => \EGroupware\Api\DateTime::server2user($start,\EGroupware\Api\DateTime::ET2), + 'date' => DateTime::server2user($start,DateTime::ET2), 'exception'=> 1 ), ); if ($extra_title) { $link['extra_title'] = $link['title']; - $link['title'] = \EGroupware\Api\DateTime::server2user($start, true); + $link['title'] = DateTime::server2user($start, true); } $user_id = ($type == 'u' ? '' : $type) . $contact['user_id']; $calendars[$user_id][$key.'_event'] = $start; @@ -2700,6 +2720,21 @@ class Contacts extends Contacts\Storage return $ctag; } + /** + * Check if given contact has a real photo attached + * + * @param array $contact + * @param string|null &$url on return vfs URL of photo + * @param int|null &$size on return size of photo + * @return bool + */ + public static function hasPhoto(array $contact, string &$url=null, int &$size=null) + { + return !empty($contact['jpegphoto']) || // LDAP/AD (not updated SQL) + ($contact['files'] & self::FILES_BIT_PHOTO) && // new SQL in VFS + ($size = filesize($url = Link::vfs_path('addressbook', $contact['id'], self::FILES_PHOTO))); + } + /** * download photo of the given ($_GET['contact_id'] or $_GET['account_id']) contact */ @@ -2718,10 +2753,7 @@ class Contacts extends Contacts\Storage $contact = $this->read($contact_id); - if (!($contact) || - empty($contact['jpegphoto']) && // LDAP/AD (not updated SQL) - !(($contact['files'] & \EGroupware\Api\Contacts::FILES_BIT_PHOTO) && // new SQL in VFS - ($size = filesize($url= \EGroupware\Api\Link::vfs_path('addressbook', $contact_id, \EGroupware\Api\Contacts::FILES_PHOTO))))) + if (!$contact || !self::hasPhoto($contact, $url, $size)) { if(!$contact_id && $id < 0) { @@ -2752,7 +2784,7 @@ class Contacts extends Contacts\Storage // different url with different etag parameter will force a reload if (isset($_GET['etag'])) { - \EGroupware\Api\Session::cache_control(30*86400); // cache for 30 days + 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) @@ -2771,7 +2803,7 @@ class Contacts extends Contacts\Storage } exit(); } - Egw::redirect(\EGroupware\Api\Image::find('addressbook','photo')); + Egw::redirect(Image::find('addressbook','photo')); } /** diff --git a/calendar/inc/class.calendar_owner_etemplate_widget.inc.php b/calendar/inc/class.calendar_owner_etemplate_widget.inc.php index a9f9684f6b..f77f69d3bb 100644 --- a/calendar/inc/class.calendar_owner_etemplate_widget.inc.php +++ b/calendar/inc/class.calendar_owner_etemplate_widget.inc.php @@ -164,6 +164,8 @@ class calendar_owner_etemplate_widget extends Etemplate\Widget\Taglist public static function ajax_search($search_text=null, array $search_options = []) { + // close session now, to not block other user actions + $GLOBALS['egw']->session->commit_session(); $bo = new calendar_bo(); @@ -193,7 +195,7 @@ class calendar_owner_etemplate_widget extends Etemplate\Widget\Taglist { $owngroup_options = $options+array('account_type'=>'owngroups'); $own_groups = Api\Accounts::link_query('',$owngroup_options); - $account_options = $options + array('account_type' => 'both'); + $account_options = $options + ['account_type' => 'both', 'tag_list' => true]; $_results += $remove_contacts = Api\Accounts::link_query($search_text, $account_options); $total += $account_options['total']; if (!empty($_REQUEST['checkgrants'])) @@ -314,11 +316,23 @@ class calendar_owner_etemplate_widget extends Etemplate\Widget\Taglist break; case 'c': case '': - $contact = $contacts_obj->read($type === '' ? 'account:'.$id : $id, true); - if (is_array($contact)) $value['icon'] = Api\Framework::link('/api/avatar.php', array( - 'contact_id' => $contact['id'], - 'etag' => $contact['etag'] ? $contact['etag'] : 1 - )); + // check if link-search already returned either icon or (l|f)name and only if not, query contact again + if (!(isset($value['icon']) || isset($value['lname']) && isset($value['fname'])) && + ($contact = $contacts_obj->read($type === '' ? 'account:'.$id : $id, true))) + { + if (Api\Contacts::hasPhoto($contact)) + { + $value['icon'] = Api\Framework::link('/api/avatar.php', array( + 'contact_id' => $contact['id'], + 'etag' => $contact['etag'] ? $contact['etag'] : 1 + )); + } + else + { + $value['lname'] = $contact['n_family']; + $value['fname'] = $contact['n_given']; + } + } if($id < 0) { $value['resources'] = array_map('strval',$GLOBALS['egw']->accounts->members($id, true));