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