1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2025-01-18 07:52:21 +01:00

Merge branch 'master' into vcategories

Conflicts:
	apps/contacts/lib/vcard.php
This commit is contained in:
Bart Visscher 2012-03-07 21:49:46 +01:00
commit cfbf2e62c1
17 changed files with 153 additions and 121 deletions

View File

@ -92,7 +92,7 @@ foreach( $add as $propname){
}
}
}
$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
$id = OC_Contacts_VCard::add($aid,$vcard);
if(!$id) {
OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
OC_Log::write('contacts','ajax/addcard.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR);

View File

@ -52,7 +52,7 @@ $vcard->setUID();
$vcard->setString('FN',$fn);
$vcard->setString('N',$n);
$id = OC_Contacts_VCard::add($aid,$vcard->serialize());
$id = OC_Contacts_VCard::add($aid,$vcard);
if(!$id) {
OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('There was an error adding the contact.'))));
OC_Log::write('contacts','ajax/addcontact.php: Recieved non-positive ID on adding card: '.$id, OC_Log::ERROR);

View File

@ -113,7 +113,7 @@ foreach ($parameters as $key=>$element) {
}
$checksum = md5($vcard->children[$line]->serialize());
if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
if(!OC_Contacts_VCard::edit($id,$vcard)) {
OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error adding contact property.'))));
OC_Log::write('contacts','ajax/addproperty.php: Error updating contact property: '.$name, OC_Log::ERROR);
exit();

View File

@ -39,7 +39,7 @@ if(is_null($line)){
unset($vcard->children[$line]);
if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
if(!OC_Contacts_VCard::edit($id,$vcard)) {
OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error deleting contact property.'))));
OC_Log::write('contacts','ajax/deleteproperty.php: Error deleting contact property', OC_Log::ERROR);
exit();

View File

@ -95,7 +95,7 @@ if(file_exists($tmp_path)) {
OC_Log::write('contacts','savecrop.php: files: Adding PHOTO property.', OC_Log::DEBUG);
$card->addProperty('PHOTO', $image->__toString(), array('ENCODING' => 'b', 'TYPE' => $image->mimeType()));
}
if(!OC_Contacts_VCard::edit($id,$card->serialize())) {
if(!OC_Contacts_VCard::edit($id,$card)) {
bailOut('Error saving contact.');
}
unlink($tmpfname);

View File

@ -122,7 +122,7 @@ switch($element) {
$checksum = md5($vcard->children[$line]->serialize());
debug('New checksum: '.$checksum);
if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
if(!OC_Contacts_VCard::edit($id,$vcard)) {
OC_JSON::error(array('data' => array('message' => OC_Contacts_App::$l10n->t('Error updating contact property.'))));
OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR);
exit();

View File

@ -80,7 +80,7 @@ foreach($missingparameters as $i){
// NOTE: This checksum is not used..?
$checksum = md5($vcard->children[$line]->serialize());
if(!OC_Contacts_VCard::edit($id,$vcard->serialize())) {
if(!OC_Contacts_VCard::edit($id,$vcard)) {
OC_JSON::error(array('data' => array('message' => $l->t('Error updating contact property.'))));
OC_Log::write('contacts','ajax/setproperty.php: Error updating contact property: '.$value, OC_Log::ERROR);
exit();

View File

@ -4,7 +4,11 @@ OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php';
OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php';
OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php';
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php';
OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php';
OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser');
OC_HOOK::connect('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents');
OC_HOOK::connect('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources');
OC_Hook::connect('OC_DAV', 'initialize', 'OC_Contacts_Hooks', 'initializeCardDAV');
OC_App::register( array(
'order' => 10,
@ -21,4 +25,4 @@ OC_App::addNavigationEntry( array(
OC_APP::registerPersonal('contacts','settings');
OC_UTIL::addScript('contacts', 'loader');
require_once('apps/contacts/lib/search.php');
OC_Search::registerProvider('OC_Search_Provider_Contacts');

View File

@ -39,7 +39,7 @@ $nodes = array(
// Fire up server
$server = new Sabre_DAV_Server($nodes);
$server->setBaseUri(OC::$WEBROOT.'/apps/contacts/carddav.php');
$server->setBaseUri(OC::$APPSWEBROOT.'/apps/contacts/carddav.php');
// Add plugins
$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
$server->addPlugin(new Sabre_CardDAV_Plugin());

View File

@ -17,8 +17,8 @@
#contacts_propertymenu li a:hover { color: #fff }
#actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;}
#card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ }
#firstrun { /*border: thin solid lightgray;*/ width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em; color:#777;}
#firstrun #selections { /*border: thin solid lightgray;*/ font-size:0.8em; width: 100%; margin: 2em auto auto auto; clear: both; }
#firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; }
#firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; }
#card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 16em; }
#card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }

View File

@ -97,11 +97,15 @@ if(is_writable('import_tmp/')){
fclose($progressfopen);
}
if(count($parts) == 1){
OC_Contacts_VCard::add($id, $file);
}else{
foreach($importready as $import){
OC_Contacts_VCard::add($id, $import);
$importready = array($file);
}
foreach($importready as $import){
$card = OC_VObject::parse($import);
if (!$card) {
OC_Log::write('contacts','Import: skipping card. Error parsing VCard: '.$import, OC_Log::ERROR);
continue; // Ditch cards that can't be parsed by Sabre.
}
OC_Contacts_VCard::add($id, $card);
}
//done the import
if(is_writable('import_tmp/')){

View File

@ -8,7 +8,7 @@ String.prototype.strip_tags = function(){
tags = this;
stripped = tags.replace(/[\<\>]/gi, "");
return stripped;
}
};
Contacts={
@ -68,7 +68,7 @@ Contacts={
return $(obj).parents('.propertycontainer').first().data('element');
},
showHideContactInfo:function() {
var show = ($('#emaillist li[class*="propertycontainer"]').length > 0 || $('#phonelist li[class*="propertycontainer"]').length > 0 || $('#addressdisplay dl[class*="propertycontainer"]').length > 0);
var show = ($('#emaillist li.propertycontainer').length > 0 || $('#phonelist li.propertycontainer').length > 0 || $('#addressdisplay dl.propertycontainer').length > 0);
console.log('showHideContactInfo: ' + show);
if(show) {
$('#contact_communication').show();
@ -82,19 +82,19 @@ Contacts={
switch (type) {
case 'EMAIL':
console.log('emails: '+$('#emaillist>li').length);
if($('#emaillist li[class*="propertycontainer"]').length == 0) {
if($('#emaillist li.propertycontainer').length == 0) {
$('#emails').hide();
}
break;
case 'TEL':
console.log('phones: '+$('#phonelist>li').length);
if($('#phonelist li[class*="propertycontainer"]').length == 0) {
if($('#phonelist li.propertycontainer').length == 0) {
$('#phones').hide();
}
break;
case 'ADR':
console.log('addresses: '+$('#addressdisplay>dl').length);
if($('#addressdisplay dl[class*="propertycontainer"]').length == 0) {
if($('#addressdisplay dl.propertycontainer').length == 0) {
$('#addresses').hide();
}
break;
@ -142,7 +142,7 @@ Contacts={
}
});
});
}
};
},
loadListHandlers:function() {
//$('.add,.delete').hide();
@ -183,7 +183,7 @@ Contacts={
dateFormat : 'dd-mm-yy'
});
// Style phone types
$('#phonelist').find('select[class*="contacts_property"]').multiselect({
$('#phonelist').find('select.contacts_property').multiselect({
noneSelectedText: t('contacts', 'Select type'),
header: false,
selectedList: 4,
@ -323,7 +323,7 @@ Contacts={
}
});
},
delete:function() {
delete: function() {
$('#contacts_deletecard').tipsy('hide');
$.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){
if(jsondata.status == 'success'){
@ -373,7 +373,7 @@ Contacts={
loadSingleProperties:function() {
var props = ['BDAY', 'NICKNAME', 'ORG'];
// Clear all elements
$('#ident .propertycontainer[class*="propertycontainer"]').each(function(){
$('#ident .propertycontainer').each(function(){
if(props.indexOf($(this).data('element')) > -1) {
$(this).data('checksum', '');
$(this).find('input').val('');
@ -448,6 +448,9 @@ Contacts={
$('#fn_select option').remove();
$('#fn_select').combobox('value', this.fn);
var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname];
if(this.data.ORG) {
names[names.length]=this.data.ORG[0].value;
}
$.each(names, function(key, value) {
$('#fn_select')
.append($('<option></option>')
@ -518,8 +521,8 @@ Contacts={
var checksum = container.data('checksum');
var name = container.data('element');
console.log('saveProperty: ' + name);
var fields = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serializeArray();
var q = container.find('input[class*="contacts_property"],select[class*="contacts_property"]').serialize();
var fields = container.find('input.contacts_property,select.contacts_property').serializeArray();
var q = container.find('input.contacts_property,select.contacts_property').serialize();
if(q == '' || q == undefined) {
console.log('Couldn\'t serialize elements.');
Contacts.UI.loading(container, false);
@ -708,7 +711,7 @@ Contacts={
},
loadAddresses:function(){
$('#addresses').hide();
$('#addressdisplay dl[class*="propertycontainer"]').remove();
$('#addressdisplay dl.propertycontainer').remove();
for(var adr in this.data.ADR) {
$('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show();
$('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer');
@ -920,15 +923,15 @@ Contacts={
},
addMail:function() {
//alert('addMail');
$('#emaillist li[class*="template"]:first-child').clone().appendTo($('#emaillist')).show();
$('#emaillist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer');
$('#emaillist li.template:first-child').clone().appendTo($('#emaillist')).show();
$('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer');
$('#emaillist li:last-child').find('input[type="email"]').focus();
Contacts.UI.loadListHandlers();
return false;
},
loadMails:function() {
$('#emails').hide();
$('#emaillist li[class*="propertycontainer"]').remove();
$('#emaillist li.propertycontainer').remove();
for(var mail in this.data.EMAIL) {
this.addMail();
//$('#emaillist li:first-child').clone().appendTo($('#emaillist')).show();
@ -950,9 +953,9 @@ Contacts={
return false;
},
addPhone:function() {
$('#phonelist li[class*="template"]:first-child').clone().appendTo($('#phonelist')); //.show();
$('#phonelist li[class*="template"]:last-child').find('select').addClass('contacts_property');
$('#phonelist li[class*="template"]:last-child').removeClass('template').addClass('propertycontainer');
$('#phonelist li.template:first-child').clone().appendTo($('#phonelist')); //.show();
$('#phonelist li.template:last-child').find('select').addClass('contacts_property');
$('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer');
$('#phonelist li:last-child').find('input[type="text"]').focus();
Contacts.UI.loadListHandlers();
$('#phonelist li:last-child').find('select').multiselect({
@ -966,7 +969,7 @@ Contacts={
},
loadPhones:function() {
$('#phones').hide();
$('#phonelist li[class*="propertycontainer"]').remove();
$('#phonelist li.propertycontainer').remove();
for(var phone in this.data.TEL) {
this.addPhone();
$('#phonelist li:last-child').find('select').multiselect('destroy');

View File

@ -85,7 +85,7 @@ class OC_Contacts_App {
$vcard = OC_VObject::parse($card['carddata']);
// Try to fix cards with missing 'N' field from pre ownCloud 4. Hot damn, this is ugly...
if(!is_null($vcard) && !$vcard->__isset('N')) {
$appinfo = $info=OC_App::getAppInfo('contacts');
$appinfo = OC_App::getAppInfo('contacts');
if($appinfo['version'] >= 5) {
OC_Log::write('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OC_Log::DEBUG);
}
@ -94,7 +94,7 @@ class OC_Contacts_App {
OC_Log::write('contacts','getContactVCard, found FN field: '.$vcard->__get('FN'), OC_Log::DEBUG);
$n = implode(';', array_reverse(array_slice(explode(' ', $vcard->__get('FN')), 0, 2))).';;;';
$vcard->setString('N', $n);
OC_Contacts_VCard::edit( $id, $vcard->serialize());
OC_Contacts_VCard::edit( $id, $vcard);
} else { // Else just add an empty 'N' field :-P
$vcard->setString('N', 'Unknown;Name;;;');
}

View File

@ -29,7 +29,7 @@ class OC_Contacts_Hooks{
* @param paramters parameters from postDeleteUser-Hook
* @return array
*/
public function deleteUser($parameters) {
static public function deleteUser($parameters) {
$addressbooks = OC_Contacts_Addressbook::all($parameters['uid']);
foreach($addressbooks as $addressbook) {
@ -38,4 +38,62 @@ class OC_Contacts_Hooks{
return true;
}
/**
* @brief Adds the CardDAV resource to the DAV server
* @param paramters parameters from initialize-Hook
* @return array
*/
static public function initializeCardDAV($parameters){
// We need a backend, the root node and the carddav plugin
$parameters['backends']['carddav'] = new OC_Connector_Sabre_CardDAV();
$parameters['nodes'][] = new Sabre_CardDAV_AddressBookRoot($parameters['backends']['principal'], $parameters['backends']['carddav']);
$parameters['plugins'][] = new Sabre_CardDAV_Plugin();
return true;
}
static public function getCalenderSources($parameters) {
$base_url = OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id=';
foreach(OC_Contacts_Addressbook::all(OC_User::getUser()) as $addressbook) {
$parameters['sources'][] =
array(
'url' => $base_url.'birthday_'. $addressbook['id'],
'backgroundColor' => '#cccccc',
'borderColor' => '#888',
'textColor' => 'black',
'cache' => true,
'editable' => false,
);
}
}
static public function getBirthdayEvents($parameters) {
$name = $parameters['calendar_id'];
if (strpos('birthday_', $name) != 0) {
return;
}
$info = explode('_', $name);
$aid = $info[1];
OC_Contacts_App::getAddressbook($aid);
foreach(OC_Contacts_VCard::all($aid) as $card){
$vcard = OC_VObject::parse($card['carddata']);
$birthday = $vcard->BDAY;
if ($birthday) {
$date = new DateTime($birthday);
$vevent = new OC_VObject('VEVENT');
$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV));
$vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE);
$vevent->setString('DURATION', 'P1D');
// DESCRIPTION?
$vevent->setString('RRULE', 'FREQ=YEARLY');
$title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday'));
$parameters['events'][] = array(
'id' => 0,//$card['id'],
'vevent' => $vevent,
'repeating' => true,
'summary' => $title,
);
}
}
}
}

View File

@ -1,6 +1,6 @@
<?php
class OC_Search_Provider_Contacts extends OC_Search_Provider{
function search($query){
class OC_Search_Provider_Contacts implements OC_Search_Provider{
static function search($query){
$addressbooks = OC_Contacts_Addressbook::all(OC_User::getUser(), 1);
// if(count($calendars)==0 || !OC_App::isEnabled('contacts')){
// //return false;
@ -26,4 +26,3 @@ class OC_Search_Provider_Contacts extends OC_Search_Provider{
return $results;
}
}
new OC_Search_Provider_Contacts();

View File

@ -174,6 +174,9 @@ class OC_Contacts_VCard{
if($property->name == 'UID'){
$uid = $property->value;
}
if($property->name == 'ORG'){
$org = $property->value;
}
if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN.
$email = $property->value;
}
@ -184,6 +187,8 @@ class OC_Contacts_VCard{
$fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
} elseif($email) {
$fn = $email;
} elseif($org) {
$fn = $org;
} else {
$fn = 'Unknown Name';
}
@ -217,32 +222,37 @@ class OC_Contacts_VCard{
/**
* @brief Adds a card
* @param integer $id Addressbook id
* @param string $data vCard file
* @return insertid on success or null if card is not parseable.
* @param integer $aid Addressbook id
* @param OC_VObject $card vCard file
* @param string $uri the uri of the card, default based on the UID
* @return insertid on success or null if no card.
*/
public static function add($id,$data){
$fn = null;
$card = OC_VObject::parse($data);
if(!is_null($card)){
OC_Contacts_App::$categories->loadFromVObject($card);
self::updateValuesFromAdd($card);
$data = $card->serialize();
}
else{
OC_Log::write('contacts','OC_Contacts_VCard::add. Error parsing VCard: '.$data,OC_Log::ERROR);
return null; // Ditch cards that can't be parsed by Sabre.
public static function add($aid, $card, $uri=null){
if(is_null($card)){
OC_Log::write('contacts','OC_Contacts_VCard::add. No vCard supplied', OC_Log::ERROR);
return null;
};
OC_Contacts_App::$categories->loadFromVObject($card);
self::updateValuesFromAdd($card);
$fn = $card->getAsString('FN');
$uid = $card->getAsString('UID');
$uri = $uid.'.vcf';
if (empty($fn)) {
$fn = null;
}
if (!$uri) {
$uid = $card->getAsString('UID');
$uri = $uid.'.vcf';
}
$data = $card->serialize();
$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
$result = $stmt->execute(array($aid,$fn,$data,$uri,time()));
$newid = OC_DB::insertid('*PREFIX*contacts_cards');
OC_Contacts_Addressbook::touch($id);
OC_Contacts_Addressbook::touch($aid);
return $newid;
}
@ -256,51 +266,33 @@ class OC_Contacts_VCard{
*/
public static function addFromDAVData($id,$uri,$data){
$card = OC_VObject::parse($data);
if(!is_null($card)){
OC_Contacts_App::$categories->loadFromVObject($card);
self::updateValuesFromAdd($card);
$data = $card->serialize();
} else {
OC_Log::write('contacts','OC_Contacts_VCard::addFromDAVData. Error parsing VCard: '.$data, OC_Log::ERROR);
return null; // Ditch cards that can't be parsed by Sabre.
};
$fn = $card->getAsString('FN');
$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
$newid = OC_DB::insertid('*PREFIX*contacts_cards');
OC_Contacts_Addressbook::touch($id);
return $newid;
return self::add($id, $data, $uri);
}
/**
* @brief edits a card
* @param integer $id id of card
* @param string $data vCard file
* @param OC_VObject $card vCard file
* @return boolean
*/
public static function edit($id, $data){
public static function edit($id, OC_VObject $card){
$oldcard = self::find($id);
$fn = null;
$card = OC_VObject::parse($data);
if(!is_null($card)){
OC_Contacts_App::$categories->loadFromVObject($card);
foreach($card->children as $property){
if($property->name == 'FN'){
$fn = $property->value;
break;
}
}
} else {
if(is_null($card)) {
return false;
}
OC_Contacts_App::$categories->loadFromVObject($card);
$fn = $card->getAsString('FN');
if (empty($fn)) {
$fn = null;
}
$now = new DateTime;
$card->setString('REV', $now->format(DateTime::W3C));
$data = $card->serialize();
$data = $card->serialize();
$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
$result = $stmt->execute(array($fn,$data,time(),$id));
@ -318,28 +310,8 @@ class OC_Contacts_VCard{
*/
public static function editFromDAVData($aid,$uri,$data){
$oldcard = self::findWhereDAVDataIs($aid,$uri);
$fn = null;
$card = OC_VObject::parse($data);
if(!is_null($card)){
OC_Contacts_App::$categories->loadFromVObject($card);
foreach($card->children as $property){
if($property->name == 'FN'){
$fn = $property->value;
break;
}
}
}
$now = new DateTime;
$card->setString('REV', $now->format(DateTime::W3C));
$data = $card->serialize();
$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
$result = $stmt->execute(array($fn,$data,time(),$oldcard['id']));
OC_Contacts_Addressbook::touch($oldcard['addressbookid']);
return true;
return self::edit($oldcard['id'], $card);
}
/**
@ -355,14 +327,6 @@ class OC_Contacts_VCard{
return true;
}
/**
* @brief Creates a UID
* @return string
*/
public static function createUID(){
return substr(md5(rand().time()),0,10);
}
/**
* @brief deletes a card with the data provided by sabredav
* @param integer $aid Addressbook id

View File

@ -3,7 +3,7 @@ $id = $_['id'];
$wattr = isset($_['width'])?'width="'.$_['width'].'"':'';
$hattr = isset($_['height'])?'height="'.$_['height'].'"':'';
?>
<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OC_Helper::linkToAbsolute('contacts', 'photo.php'); ?>?id=<?php echo $id; ?>&amp;refresh=<?php echo rand(); ?>" />
<img class="loading" id="contacts_details_photo" <?php echo $wattr; ?> <?php echo $hattr; ?> src="<?php echo OC_Helper::linkToAbsolute('contacts', 'photo.php'); ?>?id=<?php echo $id; ?>" />
<progress id="contacts_details_photo_progress" style="display:none;" value="0" max="100">0 %</progress>