]> git.mxchange.org Git - friendica.git/blob - include/Contact.php
Move Item and Conversation from Core to Object
[friendica.git] / include / Contact.php
1 <?php
2 /**
3  * @file include/Contact.php
4  */
5 use Friendica\App;
6 use Friendica\Core\PConfig;
7 use Friendica\Core\System;
8 use Friendica\Core\Worker;
9 use Friendica\Database\DBM;
10 use Friendica\Network\Probe;
11 use Friendica\Protocol\Diaspora;
12 use Friendica\Protocol\DFRN;
13 use Friendica\Protocol\OStatus;
14
15 // Included here for completeness, but this is a very dangerous operation.
16 // It is the caller's responsibility to confirm the requestor's intent and
17 // authorisation to do this.
18
19 function user_remove($uid) {
20         if (!$uid) {
21                 return;
22         }
23
24         logger('Removing user: ' . $uid);
25
26         $r = dba::select('user', array(), array('uid' => $uid), array("limit" => 1));
27
28         call_hooks('remove_user',$r);
29
30         // save username (actually the nickname as it is guaranteed
31         // unique), so it cannot be re-registered in the future.
32
33         dba::insert('userd', array('username' => $r['nickname']));
34
35         // The user and related data will be deleted in "cron_expire_and_remove_users" (cronjobs.php)
36         q("UPDATE `user` SET `account_removed` = 1, `account_expires_on` = UTC_TIMESTAMP() WHERE `uid` = %d", intval($uid));
37         Worker::add(PRIORITY_HIGH, "Notifier", "removeme", $uid);
38
39         // Send an update to the directory
40         Worker::add(PRIORITY_LOW, "Directory", $r['url']);
41
42         if($uid == local_user()) {
43                 unset($_SESSION['authenticated']);
44                 unset($_SESSION['uid']);
45                 goaway(System::baseUrl());
46         }
47 }
48
49
50 function contact_remove($id) {
51
52         // We want just to make sure that we don't delete our "self" contact
53         $r = q("SELECT `uid` FROM `contact` WHERE `id` = %d AND NOT `self` LIMIT 1",
54                 intval($id)
55         );
56         if (!DBM::is_result($r) || !intval($r[0]['uid'])) {
57                 return;
58         }
59
60         $archive = PConfig::get($r[0]['uid'], 'system','archive_removed_contacts');
61         if ($archive) {
62                 q("update contact set `archive` = 1, `network` = 'none', `writable` = 0 where id = %d",
63                         intval($id)
64                 );
65                 return;
66         }
67
68         dba::delete('contact', array('id' => $id));
69
70         // Delete the rest in the background
71         Worker::add(PRIORITY_LOW, 'RemoveContact', $id);
72 }
73
74
75 // sends an unfriend message. Does not remove the contact
76
77 function terminate_friendship($user,$self,$contact) {
78
79         /// @TODO Get rid of this, include/datetime.php should care about it by itself
80         $a = get_app();
81
82         require_once 'include/datetime.php';
83
84         if ($contact['network'] === NETWORK_OSTATUS) {
85                 // create an unfollow slap
86                 $item = array();
87                 $item['verb'] = NAMESPACE_OSTATUS."/unfollow";
88                 $item['follow'] = $contact["url"];
89                 $slap = OStatus::salmon($item, $user);
90
91                 if ((x($contact,'notify')) && (strlen($contact['notify']))) {
92                         require_once 'include/salmon.php';
93                         slapper($user,$contact['notify'],$slap);
94                 }
95         } elseif ($contact['network'] === NETWORK_DIASPORA) {
96                 Diaspora::send_unshare($user,$contact);
97         } elseif ($contact['network'] === NETWORK_DFRN) {
98                 DFRN::deliver($user,$contact,'placeholder', 1);
99         }
100
101 }
102
103
104 // Contact has refused to recognise us as a friend. We will start a countdown.
105 // If they still don't recognise us in 32 days, the relationship is over,
106 // and we won't waste any more time trying to communicate with them.
107 // This provides for the possibility that their database is temporarily messed
108 // up or some other transient event and that there's a possibility we could recover from it.
109
110 function mark_for_death($contact) {
111
112         if($contact['archive'])
113                 return;
114
115         if ($contact['term-date'] <= NULL_DATE) {
116                 q("UPDATE `contact` SET `term-date` = '%s' WHERE `id` = %d",
117                                 dbesc(datetime_convert()),
118                                 intval($contact['id'])
119                 );
120
121                 if ($contact['url'] != '') {
122                         q("UPDATE `contact` SET `term-date` = '%s'
123                                 WHERE `nurl` = '%s' AND `term-date` <= '1000-00-00'",
124                                         dbesc(datetime_convert()),
125                                         dbesc(normalise_link($contact['url']))
126                         );
127                 }
128         } else {
129
130                 /// @todo
131                 /// We really should send a notification to the owner after 2-3 weeks
132                 /// so they won't be surprised when the contact vanishes and can take
133                 /// remedial action if this was a serious mistake or glitch
134
135                 /// @todo
136                 /// Check for contact vitality via probing
137
138                 $expiry = $contact['term-date'] . ' + 32 days ';
139                 if(datetime_convert() > datetime_convert('UTC','UTC',$expiry)) {
140
141                         // relationship is really truly dead.
142                         // archive them rather than delete
143                         // though if the owner tries to unarchive them we'll start the whole process over again
144
145                         q("UPDATE `contact` SET `archive` = 1 WHERE `id` = %d",
146                                 intval($contact['id'])
147                         );
148
149                         if ($contact['url'] != '') {
150                                 q("UPDATE `contact` SET `archive` = 1 WHERE `nurl` = '%s'",
151                                         dbesc(normalise_link($contact['url']))
152                                 );
153                         }
154                 }
155         }
156
157 }
158
159 function unmark_for_death($contact) {
160
161         $r = q("SELECT `term-date` FROM `contact` WHERE `id` = %d AND (`term-date` > '%s' OR `archive`)",
162                 intval($contact['id']),
163                 dbesc('1000-00-00 00:00:00')
164         );
165
166         // We don't need to update, we never marked this contact as dead
167         if (!DBM::is_result($r)) {
168                 return;
169         }
170
171         // It's a miracle. Our dead contact has inexplicably come back to life.
172         $fields = array('term-date' => NULL_DATE, 'archive' => false);
173         dba::update('contact', $fields, array('id' => $contact['id']));
174
175         if ($contact['url'] != '') {
176                 dba::update('contact', $fields, array('nurl' => normalise_link($contact['url'])));
177         }
178 }
179
180 /**
181  * @brief Get contact data for a given profile link
182  *
183  * The function looks at several places (contact table and gcontact table) for the contact
184  * It caches its result for the same script execution to prevent duplicate calls
185  *
186  * @param string $url The profile link
187  * @param int $uid User id
188  * @param array $default If not data was found take this data as default value
189  *
190  * @return array Contact data
191  */
192 function get_contact_details_by_url($url, $uid = -1, $default = array()) {
193         static $cache = array();
194
195         if ($url == '') {
196                 return $default;
197         }
198
199         if ($uid == -1) {
200                 $uid = local_user();
201         }
202
203         if (isset($cache[$url][$uid])) {
204                 return $cache[$url][$uid];
205         }
206
207         $ssl_url = str_replace('http://', 'https://', $url);
208
209         // Fetch contact data from the contact table for the given user
210         $s = dba::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
211                         `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
212                 FROM `contact` WHERE `nurl` = ? AND `uid` = ?",
213                         normalise_link($url), $uid);
214         $r = dba::inArray($s);
215
216         // Fetch contact data from the contact table for the given user, checking with the alias
217         if (!DBM::is_result($r)) {
218                 $s = dba::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
219                                 `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
220                         FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?",
221                                 normalise_link($url), $url, $ssl_url, $uid);
222                 $r = dba::inArray($s);
223         }
224
225         // Fetch the data from the contact table with "uid=0" (which is filled automatically)
226         if (!DBM::is_result($r)) {
227                 $s = dba::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
228                         `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
229                         FROM `contact` WHERE `nurl` = ? AND `uid` = 0",
230                                 normalise_link($url));
231                 $r = dba::inArray($s);
232         }
233
234         // Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias
235         if (!DBM::is_result($r)) {
236                 $s = dba::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
237                         `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
238                         FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0",
239                                 normalise_link($url), $url, $ssl_url);
240                 $r = dba::inArray($s);
241         }
242
243         // Fetch the data from the gcontact table
244         if (!DBM::is_result($r)) {
245                 $s = dba::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
246                         `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
247                         FROM `gcontact` WHERE `nurl` = ?",
248                                 normalise_link($url));
249                 $r = dba::inArray($s);
250         }
251
252         if (DBM::is_result($r)) {
253                 // If there is more than one entry we filter out the connector networks
254                 if (count($r) > 1) {
255                         foreach ($r AS $id => $result) {
256                                 if ($result["network"] == NETWORK_STATUSNET) {
257                                         unset($r[$id]);
258                                 }
259                         }
260                 }
261
262                 $profile = array_shift($r);
263
264                 // "bd" always contains the upcoming birthday of a contact.
265                 // "birthday" might contain the birthday including the year of birth.
266                 if ($profile["birthday"] > '0001-01-01') {
267                         $bd_timestamp = strtotime($profile["birthday"]);
268                         $month = date("m", $bd_timestamp);
269                         $day = date("d", $bd_timestamp);
270
271                         $current_timestamp = time();
272                         $current_year = date("Y", $current_timestamp);
273                         $current_month = date("m", $current_timestamp);
274                         $current_day = date("d", $current_timestamp);
275
276                         $profile["bd"] = $current_year."-".$month."-".$day;
277                         $current = $current_year."-".$current_month."-".$current_day;
278
279                         if ($profile["bd"] < $current) {
280                                 $profile["bd"] = (++$current_year)."-".$month."-".$day;
281                         }
282                 } else {
283                         $profile["bd"] = '0001-01-01';
284                 }
285         } else {
286                 $profile = $default;
287         }
288
289         if (($profile["photo"] == "") && isset($default["photo"])) {
290                 $profile["photo"] = $default["photo"];
291         }
292
293         if (($profile["name"] == "") && isset($default["name"])) {
294                 $profile["name"] = $default["name"];
295         }
296
297         if (($profile["network"] == "") && isset($default["network"])) {
298                 $profile["network"] = $default["network"];
299         }
300
301         if (($profile["thumb"] == "") && isset($profile["photo"])) {
302                 $profile["thumb"] = $profile["photo"];
303         }
304
305         if (($profile["micro"] == "") && isset($profile["thumb"])) {
306                 $profile["micro"] = $profile["thumb"];
307         }
308
309         if ((($profile["addr"] == "") || ($profile["name"] == "")) && ($profile["gid"] != 0) &&
310                 in_array($profile["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS))) {
311                 Worker::add(PRIORITY_LOW, "UpdateGContact", $profile["gid"]);
312         }
313
314         // Show contact details of Diaspora contacts only if connected
315         if (($profile["cid"] == 0) && ($profile["network"] == NETWORK_DIASPORA)) {
316                 $profile["location"] = "";
317                 $profile["about"] = "";
318                 $profile["gender"] = "";
319                 $profile["birthday"] = '0001-01-01';
320         }
321
322         $cache[$url][$uid] = $profile;
323
324         return $profile;
325 }
326
327 /**
328  * @brief Get contact data for a given address
329  *
330  * The function looks at several places (contact table and gcontact table) for the contact
331  *
332  * @param string $addr The profile link
333  * @param int $uid User id
334  *
335  * @return array Contact data
336  */
337 function get_contact_details_by_addr($addr, $uid = -1) {
338         static $cache = array();
339
340         if ($addr == '') {
341                 return array();
342         }
343
344         if ($uid == -1) {
345                 $uid = local_user();
346         }
347
348         // Fetch contact data from the contact table for the given user
349         $r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
350                         `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
351                 FROM `contact` WHERE `addr` = '%s' AND `uid` = %d",
352                         dbesc($addr), intval($uid));
353
354         // Fetch the data from the contact table with "uid=0" (which is filled automatically)
355         if (!DBM::is_result($r))
356                 $r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
357                         `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
358                         FROM `contact` WHERE `addr` = '%s' AND `uid` = 0",
359                                 dbesc($addr));
360
361         // Fetch the data from the gcontact table
362         if (!DBM::is_result($r))
363                 $r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
364                         `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
365                         FROM `gcontact` WHERE `addr` = '%s'",
366                                 dbesc($addr));
367
368         if (!DBM::is_result($r)) {
369                 $data = Probe::uri($addr);
370
371                 $profile = get_contact_details_by_url($data['url'], $uid);
372         } else {
373                 $profile = $r[0];
374         }
375
376         return $profile;
377 }
378
379 if (! function_exists('contact_photo_menu')) {
380 function contact_photo_menu($contact, $uid = 0)
381 {
382         $a = get_app();
383
384         $contact_url = '';
385         $pm_url = '';
386         $status_link = '';
387         $photos_link = '';
388         $posts_link = '';
389         $contact_drop_link = '';
390         $poke_link = '';
391
392         if ($uid == 0) {
393                 $uid = local_user();
394         }
395
396         if ($contact['uid'] != $uid) {
397                 if ($uid == 0) {
398                         $profile_link = zrl($contact['url']);
399                         $menu = Array('profile' => array(t('View Profile'), $profile_link, true));
400
401                         return $menu;
402                 }
403
404                 $r = q("SELECT * FROM `contact` WHERE `nurl` = '%s' AND `network` = '%s' AND `uid` = %d",
405                         dbesc($contact['nurl']), dbesc($contact['network']), intval($uid));
406                 if ($r) {
407                         return contact_photo_menu($r[0], $uid);
408                 } else {
409                         $profile_link = zrl($contact['url']);
410                         $connlnk = 'follow/?url='.$contact['url'];
411                         $menu = array(
412                                 'profile' => array(t('View Profile'), $profile_link, true),
413                                 'follow' => array(t('Connect/Follow'), $connlnk, true)
414                         );
415
416                         return $menu;
417                 }
418         }
419
420         $sparkle = false;
421         if ($contact['network'] === NETWORK_DFRN) {
422                 $sparkle = true;
423                 $profile_link = System::baseUrl() . '/redir/' . $contact['id'];
424         } else {
425                 $profile_link = $contact['url'];
426         }
427
428         if ($profile_link === 'mailbox') {
429                 $profile_link = '';
430         }
431
432         if ($sparkle) {
433                 $status_link = $profile_link . '?url=status';
434                 $photos_link = $profile_link . '?url=photos';
435                 $profile_link = $profile_link . '?url=profile';
436         }
437
438         if (in_array($contact['network'], array(NETWORK_DFRN, NETWORK_DIASPORA))) {
439                 $pm_url = System::baseUrl() . '/message/new/' . $contact['id'];
440         }
441
442         if ($contact['network'] == NETWORK_DFRN) {
443                 $poke_link = System::baseUrl() . '/poke/?f=&c=' . $contact['id'];
444         }
445
446         $contact_url = System::baseUrl() . '/contacts/' . $contact['id'];
447
448         $posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/posts';
449         $contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
450
451         /**
452          * menu array:
453          * "name" => [ "Label", "link", (bool)Should the link opened in a new tab? ]
454          */
455         $menu = array(
456                 'status' => array(t("View Status"), $status_link, true),
457                 'profile' => array(t("View Profile"), $profile_link, true),
458                 'photos' => array(t("View Photos"), $photos_link, true),
459                 'network' => array(t("Network Posts"), $posts_link, false),
460                 'edit' => array(t("View Contact"), $contact_url, false),
461                 'drop' => array(t("Drop Contact"), $contact_drop_link, false),
462                 'pm' => array(t("Send PM"), $pm_url, false),
463                 'poke' => array(t("Poke"), $poke_link, false),
464         );
465
466
467         $args = array('contact' => $contact, 'menu' => &$menu);
468
469         call_hooks('contact_photo_menu', $args);
470
471         $menucondensed = array();
472
473         foreach ($menu AS $menuname => $menuitem) {
474                 if ($menuitem[1] != '') {
475                         $menucondensed[$menuname] = $menuitem;
476                 }
477         }
478
479         return $menucondensed;
480 }}
481
482
483 function random_profile() {
484         $r = q("SELECT `url` FROM `gcontact` WHERE `network` = '%s'
485                                 AND `last_contact` >= `last_failure`
486                                 AND `updated` > UTC_TIMESTAMP - INTERVAL 1 MONTH
487                         ORDER BY rand() LIMIT 1",
488                 dbesc(NETWORK_DFRN));
489
490         if (DBM::is_result($r))
491                 return dirname($r[0]['url']);
492         return '';
493 }
494
495
496 function contacts_not_grouped($uid,$start = 0,$count = 0) {
497
498         if(! $count) {
499                 $r = q("select count(*) as total from contact where uid = %d and self = 0 and id not in (select distinct(`contact-id`) from group_member where uid = %d) ",
500                         intval($uid),
501                         intval($uid)
502                 );
503
504                 return $r;
505
506
507         }
508
509         $r = q("select * from contact where uid = %d and self = 0 and id not in (select distinct(`contact-id`) from group_member where uid = %d) and blocked = 0 and pending = 0 limit %d, %d",
510                 intval($uid),
511                 intval($uid),
512                 intval($start),
513                 intval($count)
514         );
515
516         return $r;
517 }
518
519 /**
520  * @brief Fetch the contact id for a given url and user
521  *
522  * First lookup in the contact table to find a record matching either `url`, `nurl`,
523  * `addr` or `alias`.
524  *
525  * If there's no record and we aren't looking for a public contact, we quit.
526  * If there's one, we check that it isn't time to update the picture else we
527  * directly return the found contact id.
528  *
529  * Second, we probe the provided $url wether it's http://server.tld/profile or
530  * nick@server.tld. We quit if we can't get any info back.
531  *
532  * Third, we create the contact record if it doesn't exist
533  *
534  * Fourth, we update the existing record with the new data (avatar, alias, nick)
535  * if there's any updates
536  *
537  * @param string $url Contact URL
538  * @param integer $uid The user id for the contact (0 = public contact)
539  * @param boolean $no_update Don't update the contact
540  *
541  * @return integer Contact ID
542  */
543 function get_contact($url, $uid = 0, $no_update = false) {
544         logger("Get contact data for url ".$url." and user ".$uid." - ".System::callstack(), LOGGER_DEBUG);
545
546         $data = array();
547         $contact_id = 0;
548
549         if ($url == '') {
550                 return 0;
551         }
552
553         // We first try the nurl (http://server.tld/nick), most common case
554         $contact = dba::select('contact', array('id', 'avatar-date'), array('nurl' => normalise_link($url), 'uid' => $uid), array('limit' => 1));
555
556         // Then the addr (nick@server.tld)
557         if (!DBM::is_result($contact)) {
558                 $contact = dba::select('contact', array('id', 'avatar-date'), array('addr' => $url, 'uid' => $uid), array('limit' => 1));
559         }
560
561         // Then the alias (which could be anything)
562         if (!DBM::is_result($contact)) {
563                 // The link could be provided as http although we stored it as https
564                 $ssl_url = str_replace('http://', 'https://', $url);
565                 $r = dba::p("SELECT `id`, `avatar-date` FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ? LIMIT 1",
566                                 $url, normalise_link($url), $ssl_url, $uid);
567                 $contact = dba::fetch($r);
568                 dba::close($r);
569         }
570
571         if (DBM::is_result($contact)) {
572                 $contact_id = $contact["id"];
573
574                 // Update the contact every 7 days
575                 $update_contact = ($contact['avatar-date'] < datetime_convert('','','now -7 days'));
576
577                 // We force the update if the avatar is empty
578                 if ($contact['avatar'] == '') {
579                         $update_contact = true;
580                 }
581
582                 if (!$update_contact || $no_update) {
583                         return $contact_id;
584                 }
585         } elseif ($uid != 0) {
586                 // Non-existing user-specific contact, exiting
587                 return 0;
588         }
589
590         $data = Probe::uri($url, "", $uid);
591
592         // Last try in gcontact for unsupported networks
593         if (!in_array($data["network"], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_PUMPIO, NETWORK_MAIL))) {
594                 if ($uid != 0) {
595                         return 0;
596                 }
597
598                 // Get data from the gcontact table
599                 $gcontacts = dba::select('gcontact', array('name', 'nick', 'url', 'photo', 'addr', 'alias', 'network'),
600                                                 array('nurl' => normalise_link($url)), array('limit' => 1));
601                 if (!DBM::is_result($gcontacts)) {
602                         return 0;
603                 }
604
605                 $data = array_merge($data, $gcontacts);
606         }
607
608         if (!$contact_id && ($data["alias"] != '') && ($data["alias"] != $url)) {
609                 $contact_id = get_contact($data["alias"], $uid, true);
610         }
611
612         $url = $data["url"];
613         if (!$contact_id) {
614                 dba::insert('contact', array('uid' => $uid, 'created' => datetime_convert(), 'url' => $data["url"],
615                                         'nurl' => normalise_link($data["url"]), 'addr' => $data["addr"],
616                                         'alias' => $data["alias"], 'notify' => $data["notify"], 'poll' => $data["poll"],
617                                         'name' => $data["name"], 'nick' => $data["nick"], 'photo' => $data["photo"],
618                                         'keywords' => $data["keywords"], 'location' => $data["location"], 'about' => $data["about"],
619                                         'network' => $data["network"], 'pubkey' => $data["pubkey"],
620                                         'rel' => CONTACT_IS_SHARING, 'priority' => $data["priority"],
621                                         'batch' => $data["batch"], 'request' => $data["request"],
622                                         'confirm' => $data["confirm"], 'poco' => $data["poco"],
623                                         'name-date' => datetime_convert(), 'uri-date' => datetime_convert(),
624                                         'avatar-date' => datetime_convert(), 'writable' => 1, 'blocked' => 0,
625                                         'readonly' => 0, 'pending' => 0));
626
627                 $contacts = q("SELECT `id` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d ORDER BY `id` LIMIT 2",
628                                 dbesc(normalise_link($data["url"])),
629                                 intval($uid));
630                 if (!DBM::is_result($contacts)) {
631                         return 0;
632                 }
633
634                 $contact_id = $contacts[0]["id"];
635
636                 // Update the newly created contact from data in the gcontact table
637                 $gcontact = dba::select('gcontact', array('location', 'about', 'keywords', 'gender'),
638                                         array('nurl' => normalise_link($data["url"])), array('limit' => 1));
639                 if (DBM::is_result($gcontact)) {
640                         // Only use the information when the probing hadn't fetched these values
641                         if ($data['keywords'] != '') {
642                                 unset($gcontact['keywords']);
643                         }
644                         if ($data['location'] != '') {
645                                 unset($gcontact['location']);
646                         }
647                         if ($data['about'] != '') {
648                                 unset($gcontact['about']);
649                         }
650                         dba::update('contact', $gcontact, array('id' => $contact_id));
651                 }
652
653                 if (count($contacts) > 1 && $uid == 0 && $contact_id != 0 && $data["url"] != "") {
654                         dba::delete('contact', array("`nurl` = ? AND `uid` = 0 AND `id` != ? AND NOT `self`",
655                                 normalise_link($data["url"]), $contact_id));
656                 }
657         }
658
659         require_once "Photo.php";
660
661         update_contact_avatar($data["photo"], $uid, $contact_id);
662
663         $contact = dba::select('contact', array('url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date'),
664                                 array('id' => $contact_id), array('limit' => 1));
665
666         // This condition should always be true
667         if (!DBM::is_result($contact)) {
668                 return $contact_id;
669         }
670
671         $updated = array('addr' => $data['addr'],
672                         'alias' => $data['alias'],
673                         'url' => $data['url'],
674                         'nurl' => normalise_link($data['url']),
675                         'name' => $data['name'],
676                         'nick' => $data['nick']);
677
678         if ($data['keywords'] != '') {
679                 $updated['keywords'] = $data['keywords'];
680         }
681         if ($data['location'] != '') {
682                 $updated['location'] = $data['location'];
683         }
684         if ($data['about'] != '') {
685                 $updated['about'] = $data['about'];
686         }
687
688         if (($data["addr"] != $contact["addr"]) || ($data["alias"] != $contact["alias"])) {
689                 $updated['uri-date'] = datetime_convert();
690         }
691         if (($data["name"] != $contact["name"]) || ($data["nick"] != $contact["nick"])) {
692                 $updated['name-date'] = datetime_convert();
693         }
694
695         $updated['avatar-date'] = datetime_convert();
696
697         dba::update('contact', $updated, array('id' => $contact_id), $contact);
698
699         return $contact_id;
700 }
701
702 /**
703  * @brief Checks if the contact is blocked
704  *
705  * @param int $cid contact id
706  *
707  * @return boolean Is the contact blocked?
708  */
709 function blockedContact($cid) {
710         if ($cid == 0) {
711                 return false;
712         }
713
714         $blocked = dba::select('contact', array('blocked'), array('id' => $cid), array('limit' => 1));
715         if (!DBM::is_result($blocked)) {
716                 return false;
717         }
718         return (bool)$blocked['blocked'];
719 }
720
721 /**
722  * @brief Checks if the contact is hidden
723  *
724  * @param int $cid contact id
725  *
726  * @return boolean Is the contact hidden?
727  */
728 function hiddenContact($cid) {
729         if ($cid == 0) {
730                 return false;
731         }
732
733         $hidden = dba::select('contact', array('hidden'), array('id' => $cid), array('limit' => 1));
734         if (!DBM::is_result($hidden)) {
735                 return false;
736         }
737         return (bool)$hidden['hidden'];
738 }
739
740 /**
741  * @brief Returns posts from a given gcontact
742  *
743  * @param App $a argv application class
744  * @param int $gcontact_id Global contact
745  *
746  * @return string posts in HTML
747  */
748 function posts_from_gcontact(App $a, $gcontact_id) {
749
750         require_once 'include/conversation.php';
751
752         // There are no posts with "uid = 0" with connector networks
753         // This speeds up the query a lot
754         $r = q("SELECT `network` FROM `gcontact` WHERE `id` = %d", dbesc($gcontact_id));
755         if (in_array($r[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, "")))
756                 $sql = "(`item`.`uid` = 0 OR  (`item`.`uid` = %d AND `item`.`private`))";
757         else
758                 $sql = "`item`.`uid` = %d";
759
760         $r = q("SELECT `item`.`uri`, `item`.*, `item`.`id` AS `item_id`,
761                         `author-name` AS `name`, `owner-avatar` AS `photo`,
762                         `owner-link` AS `url`, `owner-avatar` AS `thumb`
763                 FROM `item`
764                 WHERE `gcontact-id` = %d AND $sql AND
765                         NOT `deleted` AND NOT `moderated` AND `visible`
766                 ORDER BY `item`.`created` DESC LIMIT %d, %d",
767                 intval($gcontact_id),
768                 intval(local_user()),
769                 intval($a->pager['start']),
770                 intval($a->pager['itemspage'])
771         );
772
773         $o = conversation($a, $r, 'community', false);
774
775         $o .= alt_pager($a, count($r));
776
777         return $o;
778 }
779 /**
780  * @brief Returns posts from a given contact url
781  *
782  * @param App $a argv application class
783  * @param string $contact_url Contact URL
784  *
785  * @return string posts in HTML
786  */
787 function posts_from_contact_url(App $a, $contact_url) {
788
789         require_once 'include/conversation.php';
790
791         // There are no posts with "uid = 0" with connector networks
792         // This speeds up the query a lot
793         $r = q("SELECT `network`, `id` AS `author-id`, `contact-type` FROM `contact`
794                 WHERE `contact`.`nurl` = '%s' AND `contact`.`uid` = 0",
795                 dbesc(normalise_link($contact_url)));
796
797         if (!DBM::is_result($r)) {
798                 return '';
799         }
800
801         if (in_array($r[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""))) {
802                 $sql = "(`item`.`uid` = 0 OR (`item`.`uid` = %d AND NOT `item`.`global`))";
803         } else {
804                 $sql = "`item`.`uid` = %d";
805         }
806
807         $author_id = intval($r[0]["author-id"]);
808
809         $contact = ($r[0]["contact-type"] == ACCOUNT_TYPE_COMMUNITY ? 'owner-id' : 'author-id');
810
811         $r = q(item_query()." AND `item`.`".$contact."` = %d AND ".$sql.
812                 " ORDER BY `item`.`created` DESC LIMIT %d, %d",
813                 intval($author_id),
814                 intval(local_user()),
815                 intval($a->pager['start']),
816                 intval($a->pager['itemspage'])
817         );
818
819         $o = conversation($a, $r, 'community', false);
820
821         $o .= alt_pager($a, count($r));
822
823         return $o;
824 }
825
826 /**
827  * @brief Returns a formatted location string from the given profile array
828  *
829  * @param array $profile Profile array (Generated from the "profile" table)
830  *
831  * @return string Location string
832  */
833 function formatted_location($profile) {
834         $location = '';
835
836         if($profile['locality'])
837                 $location .= $profile['locality'];
838
839         if($profile['region'] && ($profile['locality'] != $profile['region'])) {
840                 if($location)
841                         $location .= ', ';
842
843                 $location .= $profile['region'];
844         }
845
846         if($profile['country-name']) {
847                 if($location)
848                         $location .= ', ';
849
850                 $location .= $profile['country-name'];
851         }
852
853         return $location;
854 }
855
856 /**
857  * @brief Returns the account type name
858  *
859  * The function can be called with either the user or the contact array
860  *
861  * @param array $contact contact or user array
862  */
863 function account_type($contact) {
864
865         // There are several fields that indicate that the contact or user is a forum
866         // "page-flags" is a field in the user table,
867         // "forum" and "prv" are used in the contact table. They stand for PAGE_COMMUNITY and PAGE_PRVGROUP.
868         // "community" is used in the gcontact table and is true if the contact is PAGE_COMMUNITY or PAGE_PRVGROUP.
869         if((isset($contact['page-flags']) && (intval($contact['page-flags']) == PAGE_COMMUNITY))
870                 || (isset($contact['page-flags']) && (intval($contact['page-flags']) == PAGE_PRVGROUP))
871                 || (isset($contact['forum']) && intval($contact['forum']))
872                 || (isset($contact['prv']) && intval($contact['prv']))
873                 || (isset($contact['community']) && intval($contact['community'])))
874                 $type = ACCOUNT_TYPE_COMMUNITY;
875         else
876                 $type = ACCOUNT_TYPE_PERSON;
877
878         // The "contact-type" (contact table) and "account-type" (user table) are more general then the chaos from above.
879         if (isset($contact["contact-type"]))
880                 $type = $contact["contact-type"];
881         if (isset($contact["account-type"]))
882                 $type = $contact["account-type"];
883
884         switch($type) {
885                 case ACCOUNT_TYPE_ORGANISATION:
886                         $account_type = t("Organisation");
887                         break;
888                 case ACCOUNT_TYPE_NEWS:
889                         $account_type = t('News');
890                         break;
891                 case ACCOUNT_TYPE_COMMUNITY:
892                         $account_type = t("Forum");
893                         break;
894                 default:
895                         $account_type = "";
896                         break;
897         }
898
899         return $account_type;
900 }