2015-06-07 21:18:02 +00:00
< ? php
2016-03-30 22:14:51 +00:00
/**
2017-11-16 04:09:11 +00:00
* @ file src / Protocol / OStatus . php
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
namespace Friendica\Protocol ;
2016-03-30 22:14:51 +00:00
2018-01-27 01:01:32 +00:00
use Friendica\Content\Text\BBCode ;
2018-03-07 21:24:13 +00:00
use Friendica\Content\Text\HTML ;
2017-11-09 16:05:18 +00:00
use Friendica\Core\Cache ;
2017-05-07 16:58:11 +00:00
use Friendica\Core\Config ;
2018-01-22 14:54:13 +00:00
use Friendica\Core\L10n ;
2017-11-15 15:53:16 +00:00
use Friendica\Core\System ;
2017-11-08 03:57:46 +00:00
use Friendica\Database\DBM ;
2017-12-07 14:04:24 +00:00
use Friendica\Model\Contact ;
2018-01-20 23:52:54 +00:00
use Friendica\Model\Conversation ;
2018-01-25 02:08:45 +00:00
use Friendica\Model\GContact ;
2018-01-28 11:18:08 +00:00
use Friendica\Model\Item ;
2017-05-07 18:44:30 +00:00
use Friendica\Network\Probe ;
2017-12-07 13:56:11 +00:00
use Friendica\Object\Image ;
2018-01-27 02:38:34 +00:00
use Friendica\Util\DateTimeFormat ;
2017-09-17 08:01:22 +00:00
use Friendica\Util\Lock ;
2018-01-27 04:18:38 +00:00
use Friendica\Util\Network ;
2017-11-10 12:45:33 +00:00
use Friendica\Util\XML ;
2017-11-16 04:09:11 +00:00
use dba ;
use DOMDocument ;
2017-12-17 20:24:57 +00:00
use DOMXPath ;
2017-05-07 18:40:23 +00:00
2017-12-17 20:24:57 +00:00
require_once 'include/dba.php' ;
2017-05-07 18:40:23 +00:00
require_once 'include/items.php' ;
require_once 'mod/share.php' ;
require_once 'include/enotify.php' ;
require_once 'include/api.php' ;
require_once 'mod/proxy.php' ;
2015-06-20 12:40:30 +00:00
2016-03-30 22:14:51 +00:00
/**
* @ brief This class contain functions for the OStatus protocol
*/
2017-11-16 04:09:11 +00:00
class OStatus
2017-11-09 16:05:18 +00:00
{
2017-09-10 07:23:14 +00:00
private static $itemlist ;
2018-01-15 13:05:12 +00:00
private static $conv_list = [];
2017-09-10 07:23:14 +00:00
2017-09-10 07:29:24 +00:00
/**
* @ brief Fetches author data
*
2017-11-09 16:05:18 +00:00
* @ param object $xpath The xpath object
* @ param object $context The xml context of the author details
* @ param array $importer user record of the importing user
* @ param array $contact Called by reference , will contain the fetched contact
* @ param bool $onlyfetch Only fetch the header without updating the contact entries
2017-09-10 07:29:24 +00:00
*
* @ return array Array of author related entries for the item
*/
2017-11-16 04:09:11 +00:00
private static function fetchAuthor ( $xpath , $context , $importer , & $contact , $onlyfetch )
2017-11-09 16:05:18 +00:00
{
2018-01-15 13:05:12 +00:00
$author = [];
2017-09-10 07:29:24 +00:00
$author [ " author-link " ] = $xpath -> evaluate ( 'atom:author/atom:uri/text()' , $context ) -> item ( 0 ) -> nodeValue ;
$author [ " author-name " ] = $xpath -> evaluate ( 'atom:author/atom:name/text()' , $context ) -> item ( 0 ) -> nodeValue ;
$addr = $xpath -> evaluate ( 'atom:author/atom:email/text()' , $context ) -> item ( 0 ) -> nodeValue ;
$aliaslink = $author [ " author-link " ];
$alternate = $xpath -> query ( " atom:author/atom:link[@rel='alternate'] " , $context ) -> item ( 0 ) -> attributes ;
if ( is_object ( $alternate )) {
2017-11-09 16:05:18 +00:00
foreach ( $alternate as $attributes ) {
2017-09-10 07:29:24 +00:00
if (( $attributes -> name == " href " ) && ( $attributes -> textContent != " " )) {
$author [ " author-link " ] = $attributes -> textContent ;
}
}
}
$author [ " contact-id " ] = $contact [ " id " ];
2018-02-14 04:58:46 +00:00
$contact = null ;
2017-10-10 09:13:37 +00:00
if ( $aliaslink != '' ) {
2018-03-12 10:45:40 +00:00
$condition = [ " `uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?) " ,
$importer [ " uid " ], $aliaslink , NETWORK_STATUSNET ,
CONTACT_IS_SHARING , CONTACT_IS_FRIEND ];
2018-02-14 04:58:46 +00:00
$contact = dba :: selectFirst ( 'contact' , [], $condition );
2017-10-10 09:13:37 +00:00
}
2018-02-14 04:58:46 +00:00
if ( ! DBM :: is_result ( $contact ) && $author [ " author-link " ] != '' ) {
2017-09-10 07:29:24 +00:00
if ( $aliaslink == " " ) {
$aliaslink = $author [ " author-link " ];
}
2018-03-12 10:45:40 +00:00
$condition = [ " `uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?) " ,
$importer [ " uid " ], normalise_link ( $author [ " author-link " ]), normalise_link ( $aliaslink ),
NETWORK_STATUSNET , CONTACT_IS_SHARING , CONTACT_IS_FRIEND ];
2018-02-14 04:58:46 +00:00
$contact = dba :: selectFirst ( 'contact' , [], $condition );
2017-09-17 08:01:22 +00:00
}
2018-02-14 04:58:46 +00:00
if ( ! DBM :: is_result ( $contact ) && ( $addr != '' )) {
2018-03-12 10:45:40 +00:00
$condition = [ " `uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?) " ,
$importer [ " uid " ], $addr , NETWORK_STATUSNET ,
CONTACT_IS_SHARING , CONTACT_IS_FRIEND ];
2018-02-14 04:58:46 +00:00
$contact = dba :: selectFirst ( 'contact' , [], $condition );
}
2017-09-10 07:29:24 +00:00
2018-02-14 04:58:46 +00:00
if ( DBM :: is_result ( $contact )) {
if ( $contact [ 'blocked' ]) {
$contact [ 'id' ] = - 1 ;
2017-09-10 07:29:24 +00:00
}
2018-02-14 04:58:46 +00:00
$author [ " contact-id " ] = $contact [ " id " ];
2017-09-10 07:29:24 +00:00
}
2018-01-15 13:05:12 +00:00
$avatarlist = [];
2017-09-10 07:29:24 +00:00
$avatars = $xpath -> query ( " atom:author/atom:link[@rel='avatar'] " , $context );
2017-11-09 16:05:18 +00:00
foreach ( $avatars as $avatar ) {
2017-09-10 07:29:24 +00:00
$href = " " ;
$width = 0 ;
2017-11-09 16:05:18 +00:00
foreach ( $avatar -> attributes as $attributes ) {
2017-09-10 07:29:24 +00:00
if ( $attributes -> name == " href " ) {
$href = $attributes -> textContent ;
}
if ( $attributes -> name == " width " ) {
$width = $attributes -> textContent ;
}
}
if ( $href != " " ) {
$avatarlist [ $width ] = $href ;
}
}
if ( count ( $avatarlist ) > 0 ) {
krsort ( $avatarlist );
$author [ " author-avatar " ] = Probe :: fixAvatar ( current ( $avatarlist ), $author [ " author-link " ]);
}
$displayname = $xpath -> evaluate ( 'atom:author/poco:displayName/text()' , $context ) -> item ( 0 ) -> nodeValue ;
if ( $displayname != " " ) {
$author [ " author-name " ] = $displayname ;
}
$author [ " owner-name " ] = $author [ " author-name " ];
$author [ " owner-link " ] = $author [ " author-link " ];
$author [ " owner-avatar " ] = $author [ " author-avatar " ];
// Only update the contacts if it is an OStatus contact
2018-02-14 04:58:46 +00:00
if ( DBM :: is_result ( $contact ) && ( $contact [ 'id' ] > 0 ) && ! $onlyfetch && ( $contact [ " network " ] == NETWORK_OSTATUS )) {
2017-10-15 19:34:15 +00:00
2017-09-10 07:29:24 +00:00
// Update contact data
2017-10-10 09:13:37 +00:00
$current = $contact ;
unset ( $current [ 'name-date' ]);
2017-09-10 07:29:24 +00:00
// This query doesn't seem to work
// $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue;
// if ($value != "")
// $contact["notify"] = $value;
// This query doesn't seem to work as well - I hate these queries
// $value = $xpath->query("atom:link[@rel='self' and @type='application/atom+xml']", $context)->item(0)->nodeValue;
// if ($value != "")
// $contact["poll"] = $value;
2017-10-10 09:13:37 +00:00
$contact [ 'url' ] = $author [ " author-link " ];
$contact [ 'nurl' ] = normalise_link ( $contact [ 'url' ]);
2017-09-10 07:29:24 +00:00
$value = $xpath -> evaluate ( 'atom:author/atom:uri/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-11-10 05:00:50 +00:00
if ( $value != " " ) {
2017-09-10 07:29:24 +00:00
$contact [ " alias " ] = $value ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
$value = $xpath -> evaluate ( 'atom:author/poco:displayName/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-11-10 05:00:50 +00:00
if ( $value != " " ) {
2017-09-10 07:29:24 +00:00
$contact [ " name " ] = $value ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
$value = $xpath -> evaluate ( 'atom:author/poco:preferredUsername/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-11-10 05:00:50 +00:00
if ( $value != " " ) {
2017-09-10 07:29:24 +00:00
$contact [ " nick " ] = $value ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
$value = $xpath -> evaluate ( 'atom:author/poco:note/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-11-10 05:00:50 +00:00
if ( $value != " " ) {
2018-03-07 21:24:13 +00:00
$contact [ " about " ] = HTML :: toBBCode ( $value );
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
$value = $xpath -> evaluate ( 'atom:author/poco:address/poco:formatted/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-11-10 05:00:50 +00:00
if ( $value != " " ) {
2017-09-10 07:29:24 +00:00
$contact [ " location " ] = $value ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
2018-01-27 02:38:34 +00:00
$contact [ 'name-date' ] = DateTimeFormat :: utcNow ();
2017-09-10 07:29:24 +00:00
2018-01-15 13:05:12 +00:00
dba :: update ( 'contact' , $contact , [ 'id' => $contact [ " id " ]], $current );
2017-09-10 07:29:24 +00:00
2017-10-10 09:13:37 +00:00
if ( ! empty ( $author [ " author-avatar " ]) && ( $author [ " author-avatar " ] != $current [ 'avatar' ])) {
2017-09-10 07:29:24 +00:00
logger ( " Update profile picture for contact " . $contact [ " id " ], LOGGER_DEBUG );
2017-11-29 22:29:11 +00:00
Contact :: updateAvatar ( $author [ " author-avatar " ], $importer [ " uid " ], $contact [ " id " ]);
2017-09-10 07:29:24 +00:00
}
// Ensure that we are having this contact (with uid=0)
2018-03-02 00:54:45 +00:00
$cid = Contact :: getIdForURL ( $aliaslink , 0 , true );
2017-09-10 07:29:24 +00:00
if ( $cid ) {
2018-01-10 03:20:33 +00:00
$fields = [ 'url' , 'nurl' , 'name' , 'nick' , 'alias' , 'about' , 'location' ];
2018-01-10 13:36:02 +00:00
$old_contact = dba :: selectFirst ( 'contact' , $fields , [ 'id' => $cid ]);
2017-09-17 08:01:22 +00:00
2017-09-10 07:29:24 +00:00
// Update it with the current values
2018-01-15 13:05:12 +00:00
$fields = [ 'url' => $author [ " author-link " ], 'name' => $contact [ " name " ],
2017-10-10 09:13:37 +00:00
'nurl' => normalise_link ( $author [ " author-link " ]),
2017-09-17 08:01:22 +00:00
'nick' => $contact [ " nick " ], 'alias' => $contact [ " alias " ],
'about' => $contact [ " about " ], 'location' => $contact [ " location " ],
2018-01-27 02:38:34 +00:00
'success_update' => DateTimeFormat :: utcNow (), 'last-update' => DateTimeFormat :: utcNow ()];
2017-09-17 08:01:22 +00:00
2018-01-15 13:05:12 +00:00
dba :: update ( 'contact' , $fields , [ 'id' => $cid ], $old_contact );
2017-09-10 07:29:24 +00:00
// Update the avatar
2017-11-29 22:29:11 +00:00
Contact :: updateAvatar ( $author [ " author-avatar " ], 0 , $cid );
2017-09-10 07:29:24 +00:00
}
$contact [ " generation " ] = 2 ;
$contact [ " hide " ] = false ; // OStatus contacts are never hidden
$contact [ " photo " ] = $author [ " author-avatar " ];
2017-12-07 14:09:28 +00:00
$gcid = GContact :: update ( $contact );
2017-09-10 07:29:24 +00:00
2017-12-07 14:09:28 +00:00
GContact :: link ( $gcid , $contact [ " uid " ], $contact [ " id " ]);
2017-09-10 07:29:24 +00:00
}
return $author ;
}
/**
* @ brief Fetches author data from a given XML string
*
2017-11-09 16:05:18 +00:00
* @ param string $xml The XML
* @ param array $importer user record of the importing user
2017-09-10 07:29:24 +00:00
*
* @ return array Array of author related entries for the item
*/
2017-11-16 04:09:11 +00:00
public static function salmonAuthor ( $xml , $importer )
2017-11-09 16:05:18 +00:00
{
2017-11-10 05:00:50 +00:00
if ( $xml == " " ) {
2017-09-10 07:29:24 +00:00
return ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:29:24 +00:00
$doc = new DOMDocument ();
@ $doc -> loadXML ( $xml );
2017-12-17 20:24:57 +00:00
$xpath = new DOMXPath ( $doc );
2017-09-10 07:29:24 +00:00
$xpath -> registerNamespace ( 'atom' , NAMESPACE_ATOM1 );
$xpath -> registerNamespace ( 'thr' , NAMESPACE_THREAD );
$xpath -> registerNamespace ( 'georss' , NAMESPACE_GEORSS );
$xpath -> registerNamespace ( 'activity' , NAMESPACE_ACTIVITY );
$xpath -> registerNamespace ( 'media' , NAMESPACE_MEDIA );
$xpath -> registerNamespace ( 'poco' , NAMESPACE_POCO );
$xpath -> registerNamespace ( 'ostatus' , NAMESPACE_OSTATUS );
$xpath -> registerNamespace ( 'statusnet' , NAMESPACE_STATUSNET );
2018-03-13 06:21:44 +00:00
$contact = [ " id " => 0 ];
2017-09-10 07:29:24 +00:00
2018-03-13 06:21:44 +00:00
// Fetch the first author
$authordata = $xpath -> query ( '//author' ) -> item ( 0 );
$author = self :: fetchAuthor ( $xpath , $authordata , $importer , $contact , true );
return $author ;
2017-09-10 07:29:24 +00:00
}
/**
* @ brief Read attributes from element
*
* @ param object $element Element object
*
* @ return array attributes
*/
2017-11-16 04:09:11 +00:00
private static function readAttributes ( $element )
2017-11-09 16:05:18 +00:00
{
2018-01-15 13:05:12 +00:00
$attribute = [];
2017-09-10 07:29:24 +00:00
2017-11-09 16:05:18 +00:00
foreach ( $element -> attributes as $attributes ) {
2017-09-10 07:29:24 +00:00
$attribute [ $attributes -> name ] = $attributes -> textContent ;
}
return $attribute ;
}
2017-09-10 07:23:14 +00:00
/**
* @ brief Imports an XML string containing OStatus elements
*
2017-11-09 16:05:18 +00:00
* @ param string $xml The XML
* @ param array $importer user record of the importing user
* @ param array $contact contact
* @ param string $hub Called by reference , returns the fetched hub data
2017-11-23 19:01:58 +00:00
* @ return void
2017-09-10 07:23:14 +00:00
*/
2017-11-09 16:05:18 +00:00
public static function import ( $xml , $importer , & $contact , & $hub )
{
2017-09-10 07:21:23 +00:00
self :: process ( $xml , $importer , $contact , $hub );
}
2017-09-10 07:23:14 +00:00
/**
2017-09-10 07:52:07 +00:00
* @ brief Internal feed processing
2017-09-10 07:23:14 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param string $xml The XML
* @ param array $importer user record of the importing user
2017-11-23 19:01:58 +00:00
* @ param array $contact contact
2017-11-09 16:05:18 +00:00
* @ param string $hub Called by reference , returns the fetched hub data
* @ param boolean $stored Is the post fresh imported or from the database ?
2017-09-10 07:52:07 +00:00
* @ param boolean $initialize Is it the leading post so that data has to be initialized ?
*
* @ return boolean Could the XML be processed ?
2017-09-10 07:23:14 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function process ( $xml , $importer , & $contact , & $hub , $stored = false , $initialize = true )
{
2017-09-10 07:21:23 +00:00
if ( $initialize ) {
2018-01-15 13:05:12 +00:00
self :: $itemlist = [];
self :: $conv_list = [];
2017-09-10 07:21:23 +00:00
}
logger ( " Import OStatus message " , LOGGER_DEBUG );
if ( $xml == " " ) {
return false ;
}
$doc = new DOMDocument ();
@ $doc -> loadXML ( $xml );
2017-12-17 20:24:57 +00:00
$xpath = new DOMXPath ( $doc );
2017-09-10 07:21:23 +00:00
$xpath -> registerNamespace ( 'atom' , NAMESPACE_ATOM1 );
$xpath -> registerNamespace ( 'thr' , NAMESPACE_THREAD );
$xpath -> registerNamespace ( 'georss' , NAMESPACE_GEORSS );
$xpath -> registerNamespace ( 'activity' , NAMESPACE_ACTIVITY );
$xpath -> registerNamespace ( 'media' , NAMESPACE_MEDIA );
$xpath -> registerNamespace ( 'poco' , NAMESPACE_POCO );
$xpath -> registerNamespace ( 'ostatus' , NAMESPACE_OSTATUS );
$xpath -> registerNamespace ( 'statusnet' , NAMESPACE_STATUSNET );
2017-09-10 07:23:14 +00:00
$hub = " " ;
$hub_attributes = $xpath -> query ( " /atom:feed/atom:link[@rel='hub'] " ) -> item ( 0 ) -> attributes ;
if ( is_object ( $hub_attributes )) {
2017-11-09 16:05:18 +00:00
foreach ( $hub_attributes as $hub_attribute ) {
2017-09-10 07:23:14 +00:00
if ( $hub_attribute -> name == " href " ) {
$hub = $hub_attribute -> textContent ;
logger ( " Found hub " . $hub , LOGGER_DEBUG );
}
}
}
2017-09-10 07:21:23 +00:00
2018-01-15 13:05:12 +00:00
$header = [];
2017-09-10 07:21:23 +00:00
$header [ " uid " ] = $importer [ " uid " ];
$header [ " network " ] = NETWORK_OSTATUS ;
$header [ " type " ] = " remote " ;
$header [ " wall " ] = 0 ;
$header [ " origin " ] = 0 ;
$header [ " gravity " ] = GRAVITY_PARENT ;
$first_child = $doc -> firstChild -> tagName ;
if ( $first_child == " feed " ) {
$entries = $xpath -> query ( '/atom:feed/atom:entry' );
} else {
$entries = $xpath -> query ( '/atom:entry' );
2017-09-10 21:56:05 +00:00
}
if ( $entries -> length == 1 ) {
2017-09-11 12:44:37 +00:00
// We reformat the XML to make it better readable
2017-09-10 21:56:05 +00:00
$doc2 = new DOMDocument ();
$doc2 -> loadXML ( $xml );
$doc2 -> preserveWhiteSpace = false ;
$doc2 -> formatOutput = true ;
$xml2 = $doc2 -> saveXML ();
2017-09-10 07:21:23 +00:00
$header [ " protocol " ] = PROTOCOL_OSTATUS_SALMON ;
2017-09-10 21:56:05 +00:00
$header [ " source " ] = $xml2 ;
2017-09-11 12:44:37 +00:00
} elseif ( ! $initialize ) {
return false ;
2017-09-10 07:21:23 +00:00
}
// Fetch the first author
$authordata = $xpath -> query ( '//author' ) -> item ( 0 );
2017-11-16 04:09:11 +00:00
$author = self :: fetchAuthor ( $xpath , $authordata , $importer , $contact , $stored );
2017-09-10 07:21:23 +00:00
$entry = $xpath -> query ( '/atom:entry' );
// Reverse the order of the entries
2018-01-15 13:05:12 +00:00
$entrylist = [];
2017-09-10 07:21:23 +00:00
2017-11-09 16:05:18 +00:00
foreach ( $entries as $entry ) {
2017-09-10 07:21:23 +00:00
$entrylist [] = $entry ;
}
2017-11-09 16:05:18 +00:00
foreach ( array_reverse ( $entrylist ) as $entry ) {
2017-09-10 07:21:23 +00:00
// fetch the author
$authorelement = $xpath -> query ( '/atom:entry/atom:author' , $entry );
2017-09-24 15:31:09 +00:00
if ( $authorelement -> length == 0 ) {
$authorelement = $xpath -> query ( 'atom:author' , $entry );
}
2017-09-10 07:21:23 +00:00
if ( $authorelement -> length > 0 ) {
2017-11-16 04:09:11 +00:00
$author = self :: fetchAuthor ( $xpath , $entry , $importer , $contact , $stored );
2017-09-10 07:21:23 +00:00
}
$value = $xpath -> evaluate ( 'atom:author/poco:preferredUsername/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
if ( $value != " " ) {
$nickname = $value ;
} else {
$nickname = $author [ " author-name " ];
}
$item = array_merge ( $header , $author );
2017-09-17 08:01:22 +00:00
$item [ " uri " ] = $xpath -> query ( 'atom:id/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2017-09-10 07:21:23 +00:00
$item [ " verb " ] = $xpath -> query ( 'activity:verb/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2017-09-17 08:01:22 +00:00
// Delete a message
2018-01-15 13:05:12 +00:00
if ( in_array ( $item [ " verb " ], [ 'qvitter-delete-notice' , ACTIVITY_DELETE , 'delete' ])) {
2017-09-17 08:01:22 +00:00
self :: deleteNotice ( $item );
continue ;
}
2018-01-15 13:05:12 +00:00
if ( in_array ( $item [ " verb " ], [ NAMESPACE_OSTATUS . " /unfavorite " , ACTIVITY_UNFAVORITE ])) {
2017-09-17 08:01:22 +00:00
// Ignore "Unfavorite" message
logger ( " Ignore unfavorite message " . print_r ( $item , true ), LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
continue ;
}
2017-09-17 08:01:22 +00:00
// Deletions come with the same uri, so we check for duplicates after processing deletions
2018-01-15 13:05:12 +00:00
if ( dba :: exists ( 'item' , [ 'uid' => $importer [ " uid " ], 'uri' => $item [ " uri " ]])) {
2017-09-17 08:01:22 +00:00
logger ( 'Post with URI ' . $item [ " uri " ] . ' already existed for user ' . $importer [ " uid " ] . '.' , LOGGER_DEBUG );
continue ;
} else {
logger ( 'Processing post with URI ' . $item [ " uri " ] . ' for user ' . $importer [ " uid " ] . '.' , LOGGER_DEBUG );
}
2017-09-10 07:21:23 +00:00
if ( $item [ " verb " ] == ACTIVITY_JOIN ) {
// ignore "Join" messages
2017-09-17 08:01:22 +00:00
logger ( " Ignore join message " . print_r ( $item , true ), LOGGER_DEBUG );
continue ;
}
if ( $item [ " verb " ] == " http://mastodon.social/schema/1.0/block " ) {
// ignore mastodon "block" messages
logger ( " Ignore block message " . print_r ( $item , true ), LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
continue ;
}
if ( $item [ " verb " ] == ACTIVITY_FOLLOW ) {
2018-01-28 17:26:39 +00:00
Contact :: addRelationship ( $importer , $contact , $item , $nickname );
2017-09-10 07:21:23 +00:00
continue ;
}
if ( $item [ " verb " ] == NAMESPACE_OSTATUS . " /unfollow " ) {
2018-01-28 17:26:39 +00:00
Contact :: removeFollower ( $importer , $contact , $item , $dummy );
2017-09-10 07:21:23 +00:00
continue ;
}
if ( $item [ " verb " ] == ACTIVITY_FAVORITE ) {
$orig_uri = $xpath -> query ( " activity:object/atom:id " , $entry ) -> item ( 0 ) -> nodeValue ;
logger ( " Favorite " . $orig_uri . " " . print_r ( $item , true ));
$item [ " verb " ] = ACTIVITY_LIKE ;
$item [ " parent-uri " ] = $orig_uri ;
$item [ " gravity " ] = GRAVITY_LIKE ;
}
// http://activitystrea.ms/schema/1.0/rsvp-yes
2018-01-15 13:05:12 +00:00
if ( ! in_array ( $item [ " verb " ], [ ACTIVITY_POST , ACTIVITY_LIKE , ACTIVITY_SHARE ])) {
2017-09-17 08:01:22 +00:00
logger ( " Unhandled verb " . $item [ " verb " ] . " " . print_r ( $item , true ), LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
}
self :: processPost ( $xpath , $entry , $item , $importer );
if ( $initialize && ( count ( self :: $itemlist ) > 0 )) {
2017-09-14 10:00:45 +00:00
if ( self :: $itemlist [ 0 ][ 'uri' ] == self :: $itemlist [ 0 ][ 'parent-uri' ]) {
// We will import it everytime, when it is started by our contacts
$valid = ! empty ( self :: $itemlist [ 0 ][ 'contact-id' ]);
if ( ! $valid ) {
// If not, then it depends on this setting
2017-11-09 16:05:18 +00:00
$valid = ! Config :: get ( 'system' , 'ostatus_full_threads' );
2018-02-18 16:43:18 +00:00
if ( $valid ) {
logger ( " Item with uri " . self :: $itemlist [ 0 ][ 'uri' ] . " will be imported due to the system settings. " , LOGGER_DEBUG );
}
} else {
logger ( " Item with uri " . self :: $itemlist [ 0 ][ 'uri' ] . " belongs to a contact ( " . self :: $itemlist [ 0 ][ 'contact-id' ] . " ). It will be imported. " , LOGGER_DEBUG );
2017-09-14 10:00:45 +00:00
}
if ( $valid ) {
// Never post a thread when the only interaction by our contact was a like
$valid = false ;
2018-01-15 13:05:12 +00:00
$verbs = [ ACTIVITY_POST , ACTIVITY_SHARE ];
2017-11-09 16:05:18 +00:00
foreach ( self :: $itemlist as $item ) {
2017-09-14 10:00:45 +00:00
if ( ! empty ( $item [ 'contact-id' ]) && in_array ( $item [ 'verb' ], $verbs )) {
$valid = true ;
}
2017-09-10 07:21:23 +00:00
}
2018-02-18 16:43:18 +00:00
if ( $valid ) {
logger ( " Item with uri " . self :: $itemlist [ 0 ][ 'uri' ] . " will be imported since the thread contains posts or shares. " , LOGGER_DEBUG );
}
2017-09-10 07:21:23 +00:00
}
2017-09-14 10:00:45 +00:00
} else {
// But we will only import complete threads
2018-01-15 13:05:12 +00:00
$valid = dba :: exists ( 'item' , [ 'uid' => $importer [ " uid " ], 'uri' => self :: $itemlist [ 0 ][ 'parent-uri' ]]);
2018-02-18 16:43:18 +00:00
if ( $valid ) {
logger ( " Item with uri " . self :: $itemlist [ 0 ][ " uri " ] . " belongs to parent " . self :: $itemlist [ 0 ][ 'parent-uri' ] . " of user " . $importer [ " uid " ] . " . It will be imported. " , LOGGER_DEBUG );
}
2017-09-10 07:21:23 +00:00
}
if ( $valid ) {
$default_contact = 0 ;
$key = count ( self :: $itemlist );
for ( $key = count ( self :: $itemlist ) - 1 ; $key >= 0 ; $key -- ) {
if ( empty ( self :: $itemlist [ $key ][ 'contact-id' ])) {
self :: $itemlist [ $key ][ 'contact-id' ] = $default_contact ;
} else {
$default_contact = $item [ 'contact-id' ];
}
}
2017-11-09 16:05:18 +00:00
foreach ( self :: $itemlist as $item ) {
2018-01-15 13:05:12 +00:00
$found = dba :: exists ( 'item' , [ 'uid' => $importer [ " uid " ], 'uri' => $item [ " uri " ]]);
2017-09-10 07:21:23 +00:00
if ( $found ) {
2017-09-10 07:23:14 +00:00
logger ( " Item with uri " . $item [ " uri " ] . " for user " . $importer [ " uid " ] . " already exists. " , LOGGER_DEBUG );
2017-10-03 20:52:24 +00:00
} elseif ( $item [ 'contact-id' ] < 0 ) {
logger ( " Item with uri " . $item [ " uri " ] . " is from a blocked contact. " , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
} else {
2017-09-17 08:01:22 +00:00
// We are having duplicated entries. Hopefully this solves it.
2018-01-28 11:18:08 +00:00
if ( Lock :: set ( 'ostatus_process_item_insert' )) {
$ret = Item :: insert ( $item );
Lock :: remove ( 'ostatus_process_item_insert' );
2017-09-17 08:01:22 +00:00
logger ( " Item with uri " . $item [ " uri " ] . " for user " . $importer [ " uid " ] . ' stored. Return value: ' . $ret );
} else {
2018-01-28 11:18:08 +00:00
$ret = Item :: insert ( $item );
2017-09-17 08:01:22 +00:00
logger ( " We couldn't lock - but tried to store the item anyway. Return value is " . $ret );
}
2017-09-10 07:21:23 +00:00
}
}
}
2018-01-15 13:05:12 +00:00
self :: $itemlist = [];
2017-09-10 07:21:23 +00:00
}
2017-09-17 08:01:22 +00:00
logger ( 'Processing done for post with URI ' . $item [ " uri " ] . ' for user ' . $importer [ " uid " ] . '.' , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
}
return true ;
}
2017-11-23 19:01:58 +00:00
/**
* @ param object $item item
* @ return void
*/
2017-11-09 16:05:18 +00:00
private static function deleteNotice ( $item )
{
2018-01-10 03:20:33 +00:00
$condition = [ 'uid' => $item [ 'uid' ], 'author-link' => $item [ 'author-link' ], 'uri' => $item [ 'uri' ]];
2018-05-29 05:22:57 +00:00
if ( ! dba :: exists ( 'item' , $condition )) {
logger ( 'Item from ' . $item [ 'author-link' ] . ' with uri ' . $item [ 'uri' ] . ' for user ' . $item [ 'uid' ] . " wasn't found. We don't delete it. " );
2017-09-17 08:01:22 +00:00
return ;
}
2018-05-29 05:22:57 +00:00
Item :: delete ( $condition );
2017-09-17 08:01:22 +00:00
logger ( 'Deleted item with uri ' . $item [ 'uri' ] . ' for user ' . $item [ 'uid' ]);
}
2017-09-10 07:52:07 +00:00
/**
* @ brief Processes the XML for a post
*
2017-11-09 16:05:18 +00:00
* @ param object $xpath The xpath object
* @ param object $entry The xml entry that is processed
* @ param array $item The item array
* @ param array $importer user record of the importing user
2017-11-23 19:01:58 +00:00
* @ return void
2017-09-10 07:52:07 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function processPost ( $xpath , $entry , & $item , $importer )
{
2018-03-07 21:24:13 +00:00
$item [ " body " ] = HTML :: toBBCode ( $xpath -> query ( 'atom:content/text()' , $entry ) -> item ( 0 ) -> nodeValue );
2017-09-10 07:21:23 +00:00
$item [ " object-type " ] = $xpath -> query ( 'activity:object-type/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
if (( $item [ " object-type " ] == ACTIVITY_OBJ_BOOKMARK ) || ( $item [ " object-type " ] == ACTIVITY_OBJ_EVENT )) {
$item [ " title " ] = $xpath -> query ( 'atom:title/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$item [ " body " ] = $xpath -> query ( 'atom:summary/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
} elseif ( $item [ " object-type " ] == ACTIVITY_OBJ_QUESTION ) {
$item [ " title " ] = $xpath -> query ( 'atom:title/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
}
$item [ " created " ] = $xpath -> query ( 'atom:published/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$item [ " edited " ] = $xpath -> query ( 'atom:updated/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$conversation = $xpath -> query ( 'ostatus:conversation/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$item [ 'conversation-uri' ] = $conversation ;
$conv = $xpath -> query ( 'ostatus:conversation' , $entry );
if ( is_object ( $conv -> item ( 0 ))) {
2017-11-09 16:05:18 +00:00
foreach ( $conv -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 07:21:23 +00:00
if ( $attributes -> name == " ref " ) {
$item [ 'conversation-uri' ] = $attributes -> textContent ;
}
if ( $attributes -> name == " href " ) {
$item [ 'conversation-href' ] = $attributes -> textContent ;
}
}
}
$related = " " ;
$inreplyto = $xpath -> query ( 'thr:in-reply-to' , $entry );
if ( is_object ( $inreplyto -> item ( 0 ))) {
2017-11-09 16:05:18 +00:00
foreach ( $inreplyto -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 07:21:23 +00:00
if ( $attributes -> name == " ref " ) {
$item [ " parent-uri " ] = $attributes -> textContent ;
}
if ( $attributes -> name == " href " ) {
$related = $attributes -> textContent ;
}
}
}
$georsspoint = $xpath -> query ( 'georss:point' , $entry );
if ( ! empty ( $georsspoint ) && ( $georsspoint -> length > 0 )) {
$item [ " coord " ] = $georsspoint -> item ( 0 ) -> nodeValue ;
}
$categories = $xpath -> query ( 'atom:category' , $entry );
if ( $categories ) {
2017-11-09 16:05:18 +00:00
foreach ( $categories as $category ) {
foreach ( $category -> attributes as $attributes ) {
2017-09-10 07:21:23 +00:00
if ( $attributes -> name == " term " ) {
$term = $attributes -> textContent ;
if ( strlen ( $item [ " tag " ])) {
$item [ " tag " ] .= ',' ;
}
$item [ " tag " ] .= " #[url= " . System :: baseUrl () . " /search?tag= " . $term . " ] " . $term . " [/url] " ;
}
}
}
}
$self = '' ;
$add_body = '' ;
$links = $xpath -> query ( 'atom:link' , $entry );
if ( $links ) {
$link_data = self :: processLinks ( $links , $item );
$self = $link_data [ 'self' ];
$add_body = $link_data [ 'add_body' ];
}
$repeat_of = " " ;
$notice_info = $xpath -> query ( 'statusnet:notice_info' , $entry );
if ( $notice_info && ( $notice_info -> length > 0 )) {
2017-11-09 16:05:18 +00:00
foreach ( $notice_info -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 07:21:23 +00:00
if ( $attributes -> name == " source " ) {
$item [ " app " ] = strip_tags ( $attributes -> textContent );
}
if ( $attributes -> name == " repeat_of " ) {
$repeat_of = $attributes -> textContent ;
}
}
}
// Is it a repeated post?
if (( $repeat_of != " " ) || ( $item [ " verb " ] == ACTIVITY_SHARE )) {
$link_data = self :: processRepeatedItem ( $xpath , $entry , $item , $importer );
if ( ! empty ( $link_data [ 'add_body' ])) {
$add_body .= $link_data [ 'add_body' ];
}
}
$item [ " body " ] .= $add_body ;
// Only add additional data when there is no picture in the post
2017-11-09 16:05:18 +00:00
if ( ! strstr ( $item [ " body " ], '[/img]' )) {
2017-09-10 07:21:23 +00:00
$item [ " body " ] = add_page_info_to_body ( $item [ " body " ]);
}
// Mastodon Content Warning
if (( $item [ " verb " ] == ACTIVITY_POST ) && $xpath -> evaluate ( 'boolean(atom:summary)' , $entry )) {
$clear_text = $xpath -> query ( 'atom:summary/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2018-03-13 21:58:05 +00:00
if ( ! empty ( $clear_text )) {
2018-03-23 10:15:55 +00:00
$item [ 'content-warning' ] = HTML :: toBBCode ( $clear_text );
2018-03-13 21:58:05 +00:00
}
2017-09-10 07:21:23 +00:00
}
2017-09-11 12:44:37 +00:00
if (( $self != '' ) && empty ( $item [ 'protocol' ])) {
self :: fetchSelf ( $self , $item );
}
2017-09-10 21:56:05 +00:00
if ( ! empty ( $item [ " conversation-href " ])) {
self :: fetchConversation ( $item [ 'conversation-href' ], $item [ 'conversation-uri' ]);
}
2018-03-15 21:33:28 +00:00
if ( isset ( $item [ " parent-uri " ])) {
2018-01-15 13:05:12 +00:00
if ( ! dba :: exists ( 'item' , [ 'uid' => $importer [ " uid " ], 'uri' => $item [ 'parent-uri' ]])) {
2018-03-15 21:33:28 +00:00
if ( $related != '' ) {
self :: fetchRelated ( $related , $item [ " parent-uri " ], $importer );
}
2017-09-14 19:23:14 +00:00
} else {
2017-09-17 08:01:22 +00:00
logger ( 'Reply with URI ' . $item [ " uri " ] . ' already existed for user ' . $importer [ " uid " ] . '.' , LOGGER_DEBUG );
2017-09-14 19:23:14 +00:00
}
2017-09-10 07:21:23 +00:00
$item [ " type " ] = 'remote-comment' ;
$item [ " gravity " ] = GRAVITY_COMMENT ;
} else {
$item [ " parent-uri " ] = $item [ " uri " ];
}
2017-09-11 12:44:37 +00:00
if (( $item [ 'author-link' ] != '' ) && ! empty ( $item [ 'protocol' ])) {
2018-01-20 23:52:54 +00:00
$item = Conversation :: insert ( $item );
2017-09-10 07:21:23 +00:00
}
self :: $itemlist [] = $item ;
}
2017-09-10 21:56:05 +00:00
/**
* @ brief Fetch the conversation for posts
*
2017-11-09 16:05:18 +00:00
* @ param string $conversation The link to the conversation
2017-09-10 21:56:05 +00:00
* @ param string $conversation_uri The conversation in " uri " format
2017-11-23 19:01:58 +00:00
* @ return void
2017-09-10 21:56:05 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function fetchConversation ( $conversation , $conversation_uri )
{
2017-09-10 21:56:05 +00:00
// Ensure that we only store a conversation once in a process
if ( isset ( self :: $conv_list [ $conversation ])) {
return ;
}
self :: $conv_list [ $conversation ] = true ;
2018-01-27 16:13:41 +00:00
$conversation_data = Network :: curl ( $conversation , false , $redirects , [ 'accept_content' => 'application/atom+xml, text/html' ]);
2017-09-10 21:56:05 +00:00
if ( ! $conversation_data [ 'success' ]) {
return ;
}
$xml = '' ;
if ( stristr ( $conversation_data [ 'header' ], 'Content-Type: application/atom+xml' )) {
$xml = $conversation_data [ 'body' ];
}
if ( $xml == '' ) {
$doc = new DOMDocument ();
if ( !@ $doc -> loadHTML ( $conversation_data [ 'body' ])) {
return ;
}
2017-12-17 20:24:57 +00:00
$xpath = new DOMXPath ( $doc );
2017-09-10 21:56:05 +00:00
$links = $xpath -> query ( '//link' );
if ( $links ) {
2018-02-14 04:58:46 +00:00
$file = '' ;
2017-11-09 16:05:18 +00:00
foreach ( $links as $link ) {
2017-11-16 04:09:11 +00:00
$attribute = self :: readAttributes ( $link );
2017-09-10 21:56:05 +00:00
if (( $attribute [ 'rel' ] == 'alternate' ) && ( $attribute [ 'type' ] == 'application/atom+xml' )) {
$file = $attribute [ 'href' ];
}
}
if ( $file != '' ) {
2018-01-27 16:13:41 +00:00
$conversation_atom = Network :: curl ( $attribute [ 'href' ]);
2017-09-10 21:56:05 +00:00
if ( $conversation_atom [ 'success' ]) {
$xml = $conversation_atom [ 'body' ];
}
}
}
}
if ( $xml == '' ) {
return ;
}
self :: storeConversation ( $xml , $conversation , $conversation_uri );
}
/**
* @ brief Store a feed in several conversation entries
*
2017-11-09 16:05:18 +00:00
* @ param string $xml The feed
* @ param string $conversation conversation
* @ param string $conversation_uri conversation uri
2017-11-23 19:01:58 +00:00
* @ return void
2017-09-10 21:56:05 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function storeConversation ( $xml , $conversation = '' , $conversation_uri = '' )
{
2017-09-10 21:56:05 +00:00
$doc = new DOMDocument ();
@ $doc -> loadXML ( $xml );
2017-12-17 20:24:57 +00:00
$xpath = new DOMXPath ( $doc );
2017-09-10 21:56:05 +00:00
$xpath -> registerNamespace ( 'atom' , NAMESPACE_ATOM1 );
$xpath -> registerNamespace ( 'thr' , NAMESPACE_THREAD );
$xpath -> registerNamespace ( 'ostatus' , NAMESPACE_OSTATUS );
$entries = $xpath -> query ( '/atom:feed/atom:entry' );
// Now store the entries
2017-11-09 16:05:18 +00:00
foreach ( $entries as $entry ) {
2017-09-10 21:56:05 +00:00
$doc2 = new DOMDocument ();
$doc2 -> preserveWhiteSpace = false ;
$doc2 -> formatOutput = true ;
2018-01-15 13:05:12 +00:00
$conv_data = [];
2017-09-10 21:56:05 +00:00
$conv_data [ 'protocol' ] = PROTOCOL_SPLITTED_CONV ;
$conv_data [ 'network' ] = NETWORK_OSTATUS ;
$conv_data [ 'uri' ] = $xpath -> query ( 'atom:id/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$inreplyto = $xpath -> query ( 'thr:in-reply-to' , $entry );
if ( is_object ( $inreplyto -> item ( 0 ))) {
2017-11-09 16:05:18 +00:00
foreach ( $inreplyto -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 21:56:05 +00:00
if ( $attributes -> name == " ref " ) {
$conv_data [ 'reply-to-uri' ] = $attributes -> textContent ;
}
}
}
$conv = $xpath -> query ( 'ostatus:conversation/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
$conv_data [ 'conversation-uri' ] = $conv ;
$conv = $xpath -> query ( 'ostatus:conversation' , $entry );
if ( is_object ( $conv -> item ( 0 ))) {
2017-11-09 16:05:18 +00:00
foreach ( $conv -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 21:56:05 +00:00
if ( $attributes -> name == " ref " ) {
$conv_data [ 'conversation-uri' ] = $attributes -> textContent ;
}
if ( $attributes -> name == " href " ) {
$conv_data [ 'conversation-href' ] = $attributes -> textContent ;
}
}
}
if ( $conversation != '' ) {
$conv_data [ 'conversation-uri' ] = $conversation ;
}
if ( $conversation_uri != '' ) {
$conv_data [ 'conversation-uri' ] = $conversation_uri ;
}
$entry = $doc2 -> importNode ( $entry , true );
$doc2 -> appendChild ( $entry );
$conv_data [ 'source' ] = $doc2 -> saveXML ();
2018-01-15 13:05:12 +00:00
$condition = [ 'item-uri' => $conv_data [ 'uri' ], 'protocol' => PROTOCOL_OSTATUS_FEED ];
2017-09-11 12:44:37 +00:00
if ( dba :: exists ( 'conversation' , $condition )) {
logger ( 'Delete deprecated entry for URI ' . $conv_data [ 'uri' ], LOGGER_DEBUG );
2018-01-15 13:05:12 +00:00
dba :: delete ( 'conversation' , [ 'item-uri' => $conv_data [ 'uri' ]]);
2017-09-11 12:44:37 +00:00
}
2017-09-10 21:56:05 +00:00
logger ( 'Store conversation data for uri ' . $conv_data [ 'uri' ], LOGGER_DEBUG );
2018-01-20 23:52:54 +00:00
Conversation :: insert ( $conv_data );
2017-09-10 21:56:05 +00:00
}
}
2017-09-11 12:44:37 +00:00
/**
* @ brief Fetch the own post so that it can be stored later
*
* We want to store the original data for later processing .
* This function is meant for cases where we process a feed with multiple entries .
* In that case we need to fetch the single posts here .
*
* @ param string $self The link to the self item
2017-11-23 19:01:58 +00:00
* @ param array $item The item array
* @ return void
2017-09-11 12:44:37 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function fetchSelf ( $self , & $item )
{
2018-01-15 13:05:12 +00:00
$condition = [ '`item-uri` = ? AND `protocol` IN (?, ?)' , $self , PROTOCOL_DFRN , PROTOCOL_OSTATUS_SALMON ];
2017-09-11 12:44:37 +00:00
if ( dba :: exists ( 'conversation' , $condition )) {
2017-09-17 08:01:22 +00:00
logger ( 'Conversation ' . $item [ 'uri' ] . ' is already stored.' , LOGGER_DEBUG );
2017-09-11 12:44:37 +00:00
return ;
}
2018-01-27 16:13:41 +00:00
$self_data = Network :: curl ( $self );
2017-09-11 12:44:37 +00:00
if ( ! $self_data [ 'success' ]) {
return ;
}
// We reformat the XML to make it better readable
$doc = new DOMDocument ();
$doc -> loadXML ( $self_data [ 'body' ]);
$doc -> preserveWhiteSpace = false ;
$doc -> formatOutput = true ;
$xml = $doc -> saveXML ();
$item [ " protocol " ] = PROTOCOL_OSTATUS_SALMON ;
$item [ " source " ] = $xml ;
2017-09-17 08:01:22 +00:00
logger ( 'Conversation ' . $item [ 'uri' ] . ' is now fetched.' , LOGGER_DEBUG );
2017-09-11 12:44:37 +00:00
}
2017-09-10 07:52:07 +00:00
/**
* @ brief Fetch related posts and processes them
*
2017-11-09 16:05:18 +00:00
* @ param string $related The link to the related item
2017-09-10 07:52:07 +00:00
* @ param string $related_uri The related item in " uri " format
2017-11-09 16:05:18 +00:00
* @ param array $importer user record of the importing user
2017-11-23 19:01:58 +00:00
* @ return void
2017-09-10 07:52:07 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function fetchRelated ( $related , $related_uri , $importer )
{
2018-01-10 03:20:33 +00:00
$condition = [ '`item-uri` = ? AND `protocol` IN (?, ?)' , $related_uri , PROTOCOL_DFRN , PROTOCOL_OSTATUS_SALMON ];
2018-01-10 13:36:02 +00:00
$conversation = dba :: selectFirst ( 'conversation' , [ 'source' , 'protocol' ], $condition );
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $conversation )) {
2017-09-10 07:21:23 +00:00
$stored = true ;
$xml = $conversation [ 'source' ];
if ( self :: process ( $xml , $importer , $contact , $hub , $stored , false )) {
2017-09-10 08:27:24 +00:00
logger ( 'Got valid cached XML for URI ' . $related_uri , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
return ;
}
if ( $conversation [ 'protocol' ] == PROTOCOL_OSTATUS_SALMON ) {
2017-09-10 08:27:24 +00:00
logger ( 'Delete invalid cached XML for URI ' . $related_uri , LOGGER_DEBUG );
2018-01-15 13:05:12 +00:00
dba :: delete ( 'conversation' , [ 'item-uri' => $related_uri ]);
2017-09-10 07:21:23 +00:00
}
}
$stored = false ;
2018-01-27 16:13:41 +00:00
$related_data = Network :: curl ( $related , false , $redirects , [ 'accept_content' => 'application/atom+xml, text/html' ]);
2017-09-10 07:21:23 +00:00
if ( ! $related_data [ 'success' ]) {
return ;
}
$xml = '' ;
if ( stristr ( $related_data [ 'header' ], 'Content-Type: application/atom+xml' )) {
2017-09-10 08:27:24 +00:00
logger ( 'Directly fetched XML for URI ' . $related_uri , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
$xml = $related_data [ 'body' ];
}
if ( $xml == '' ) {
$doc = new DOMDocument ();
if ( !@ $doc -> loadHTML ( $related_data [ 'body' ])) {
return ;
}
2017-12-17 20:24:57 +00:00
$xpath = new DOMXPath ( $doc );
2017-09-10 07:21:23 +00:00
2017-09-10 08:27:24 +00:00
$atom_file = '' ;
2017-09-10 07:21:23 +00:00
$links = $xpath -> query ( '//link' );
if ( $links ) {
2017-11-09 16:05:18 +00:00
foreach ( $links as $link ) {
2017-11-16 04:09:11 +00:00
$attribute = self :: readAttributes ( $link );
2017-09-10 07:21:23 +00:00
if (( $attribute [ 'rel' ] == 'alternate' ) && ( $attribute [ 'type' ] == 'application/atom+xml' )) {
2017-09-10 08:27:24 +00:00
$atom_file = $attribute [ 'href' ];
}
}
if ( $atom_file != '' ) {
2018-01-27 16:13:41 +00:00
$related_atom = Network :: curl ( $atom_file );
2017-09-10 07:21:23 +00:00
2017-09-10 08:27:24 +00:00
if ( $related_atom [ 'success' ]) {
logger ( 'Fetched XML for URI ' . $related_uri , LOGGER_DEBUG );
$xml = $related_atom [ 'body' ];
2017-09-10 07:21:23 +00:00
}
}
}
}
// Workaround for older GNU Social servers
if (( $xml == '' ) && strstr ( $related , '/notice/' )) {
2018-01-27 16:13:41 +00:00
$related_atom = Network :: curl ( str_replace ( '/notice/' , '/api/statuses/show/' , $related ) . '.atom' );
2017-09-10 08:27:24 +00:00
if ( $related_atom [ 'success' ]) {
logger ( 'GNU Social workaround to fetch XML for URI ' . $related_uri , LOGGER_DEBUG );
$xml = $related_atom [ 'body' ];
}
}
// Even more worse workaround for GNU Social ;-)
if ( $xml == '' ) {
2017-11-16 04:09:11 +00:00
$related_guess = OStatus :: convertHref ( $related_uri );
2018-01-27 16:13:41 +00:00
$related_atom = Network :: curl ( str_replace ( '/notice/' , '/api/statuses/show/' , $related_guess ) . '.atom' );
2017-09-10 07:21:23 +00:00
if ( $related_atom [ 'success' ]) {
2017-09-10 08:27:24 +00:00
logger ( 'GNU Social workaround 2 to fetch XML for URI ' . $related_uri , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
$xml = $related_atom [ 'body' ];
}
}
2017-09-10 21:56:05 +00:00
// Finally we take the data that we fetched from "ostatus:conversation"
if ( $xml == '' ) {
2018-01-10 03:20:33 +00:00
$condition = [ 'item-uri' => $related_uri , 'protocol' => PROTOCOL_SPLITTED_CONV ];
2018-01-10 13:36:02 +00:00
$conversation = dba :: selectFirst ( 'conversation' , [ 'source' ], $condition );
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $conversation )) {
2017-09-10 21:56:05 +00:00
$stored = true ;
logger ( 'Got cached XML from conversation for URI ' . $related_uri , LOGGER_DEBUG );
$xml = $conversation [ 'source' ];
}
}
2017-09-10 07:21:23 +00:00
if ( $xml != '' ) {
self :: process ( $xml , $importer , $contact , $hub , $stored , false );
2017-09-10 08:27:24 +00:00
} else {
logger ( " XML couldn't be fetched for URI: " . $related_uri . " - href: " . $related , LOGGER_DEBUG );
2017-09-10 07:21:23 +00:00
}
return ;
}
2017-09-10 07:52:07 +00:00
/**
* @ brief Processes the XML for a repeated post
*
2017-11-09 16:05:18 +00:00
* @ param object $xpath The xpath object
* @ param object $entry The xml entry that is processed
* @ param array $item The item array
* @ param array $importer user record of the importing user
2017-09-10 07:52:07 +00:00
*
* @ return array with data from links
*/
2017-11-09 16:05:18 +00:00
private static function processRepeatedItem ( $xpath , $entry , & $item , $importer )
{
2017-09-10 07:21:23 +00:00
$activityobjects = $xpath -> query ( 'activity:object' , $entry ) -> item ( 0 );
if ( ! is_object ( $activityobjects )) {
2018-01-15 13:05:12 +00:00
return [];
2017-09-10 07:21:23 +00:00
}
2018-01-15 13:05:12 +00:00
$link_data = [];
2017-09-10 07:21:23 +00:00
$orig_uri = $xpath -> query ( 'atom:id/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
$links = $xpath -> query ( " atom:link " , $activityobjects );
if ( $links ) {
$link_data = self :: processLinks ( $links , $item );
}
$orig_body = $xpath -> query ( 'atom:content/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
$orig_created = $xpath -> query ( 'atom:published/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
$orig_edited = $xpath -> query ( 'atom:updated/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-12-17 21:32:35 +00:00
$orig_author = self :: fetchAuthor ( $xpath , $activityobjects , $importer , $dummy , false );
2017-09-10 07:21:23 +00:00
$item [ " author-name " ] = $orig_author [ " author-name " ];
$item [ " author-link " ] = $orig_author [ " author-link " ];
$item [ " author-avatar " ] = $orig_author [ " author-avatar " ];
2018-03-07 21:24:13 +00:00
$item [ " body " ] = HTML :: toBBCode ( $orig_body );
2017-09-10 07:21:23 +00:00
$item [ " created " ] = $orig_created ;
$item [ " edited " ] = $orig_edited ;
$item [ " uri " ] = $orig_uri ;
$item [ " verb " ] = $xpath -> query ( 'activity:verb/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
$item [ " object-type " ] = $xpath -> query ( 'activity:object-type/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
$inreplyto = $xpath -> query ( 'thr:in-reply-to' , $activityobjects );
if ( is_object ( $inreplyto -> item ( 0 ))) {
2017-11-09 16:05:18 +00:00
foreach ( $inreplyto -> item ( 0 ) -> attributes as $attributes ) {
2017-09-10 07:21:23 +00:00
if ( $attributes -> name == " ref " ) {
$item [ " parent-uri " ] = $attributes -> textContent ;
}
}
}
return $link_data ;
}
2017-09-10 07:52:07 +00:00
/**
* @ brief Processes links in the XML
*
* @ param object $links The xml data that contain links
2017-11-09 16:05:18 +00:00
* @ param array $item The item array
2017-09-10 07:52:07 +00:00
*
* @ return array with data from the links
*/
2017-11-09 16:05:18 +00:00
private static function processLinks ( $links , & $item )
{
2018-01-15 13:05:12 +00:00
$link_data = [ 'add_body' => '' , 'self' => '' ];
2017-09-10 07:21:23 +00:00
2017-11-09 16:05:18 +00:00
foreach ( $links as $link ) {
2017-11-16 04:09:11 +00:00
$attribute = self :: readAttributes ( $link );
2017-09-10 07:21:23 +00:00
if (( $attribute [ 'rel' ] != " " ) && ( $attribute [ 'href' ] != " " )) {
switch ( $attribute [ 'rel' ]) {
case " alternate " :
$item [ " plink " ] = $attribute [ 'href' ];
2017-11-09 16:05:18 +00:00
if (( $item [ " object-type " ] == ACTIVITY_OBJ_QUESTION )
|| ( $item [ " object-type " ] == ACTIVITY_OBJ_EVENT )
) {
2017-09-10 07:21:23 +00:00
$item [ " body " ] .= add_page_info ( $attribute [ 'href' ]);
}
break ;
case " ostatus:conversation " :
$link_data [ 'conversation' ] = $attribute [ 'href' ];
$item [ 'conversation-href' ] = $link_data [ 'conversation' ];
if ( ! isset ( $item [ 'conversation-uri' ])) {
$item [ 'conversation-uri' ] = $item [ 'conversation-href' ];
}
break ;
case " enclosure " :
2017-11-09 16:05:18 +00:00
$filetype = strtolower ( substr ( $attribute [ 'type' ], 0 , strpos ( $attribute [ 'type' ], '/' )));
2017-09-10 07:21:23 +00:00
if ( $filetype == 'image' ) {
$link_data [ 'add_body' ] .= " \n [img] " . $attribute [ 'href' ] . '[/img]' ;
} else {
if ( strlen ( $item [ " attach " ])) {
$item [ " attach " ] .= ',' ;
}
if ( ! isset ( $attribute [ 'length' ])) {
$attribute [ 'length' ] = " 0 " ;
}
$item [ " attach " ] .= '[attach]href="' . $attribute [ 'href' ] . '" length="' . $attribute [ 'length' ] . '" type="' . $attribute [ 'type' ] . '" title="' . $attribute [ 'title' ] . '"[/attach]' ;
}
break ;
case " related " :
if ( $item [ " object-type " ] != ACTIVITY_OBJ_BOOKMARK ) {
if ( ! isset ( $item [ " parent-uri " ])) {
$item [ " parent-uri " ] = $attribute [ 'href' ];
}
$link_data [ 'related' ] = $attribute [ 'href' ];
} else {
$item [ " body " ] .= add_page_info ( $attribute [ 'href' ]);
}
break ;
case " self " :
if ( $item [ " plink " ] == '' ) {
$item [ " plink " ] = $attribute [ 'href' ];
}
$link_data [ 'self' ] = $attribute [ 'href' ];
break ;
}
}
}
return $link_data ;
}
2017-11-09 16:05:18 +00:00
/**
2017-09-10 07:58:14 +00:00
* @ brief Create an url out of an uri
*
* @ param string $href URI in the format " parameter1:parameter1:... "
*
* @ return string URL in the format http ( s ) ://....
*/
2017-11-16 04:09:11 +00:00
public static function convertHref ( $href )
2017-11-09 16:05:18 +00:00
{
$elements = explode ( " : " , $href );
2017-09-10 07:58:14 +00:00
2017-11-10 05:00:50 +00:00
if (( count ( $elements ) <= 2 ) || ( $elements [ 0 ] != " tag " )) {
2017-09-10 07:58:14 +00:00
return $href ;
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:58:14 +00:00
$server = explode ( " , " , $elements [ 1 ]);
$conversation = explode ( " = " , $elements [ 2 ]);
2017-11-10 05:00:50 +00:00
if (( count ( $elements ) == 4 ) && ( $elements [ 2 ] == " post " )) {
2017-09-10 07:58:14 +00:00
return " http:// " . $server [ 0 ] . " /notice/ " . $elements [ 3 ];
2017-11-10 05:00:50 +00:00
}
2017-09-10 07:58:14 +00:00
if (( count ( $conversation ) != 2 ) || ( $conversation [ 1 ] == " " )) {
return $href ;
}
if ( $elements [ 3 ] == " objectType=thread " ) {
return " http:// " . $server [ 0 ] . " /conversation/ " . $conversation [ 1 ];
} else {
return " http:// " . $server [ 0 ] . " /notice/ " . $conversation [ 1 ];
}
return $href ;
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 05:34:13 +00:00
* @ brief Checks if the current post is a reshare
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ param array $item The item array of thw post
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ return string The guid if the post is a reshare
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function getResharedGuid ( $item )
2017-11-09 16:05:18 +00:00
{
2016-03-30 21:25:20 +00:00
$body = trim ( $item [ " body " ]);
2015-12-10 00:10:54 +00:00
2016-03-30 21:25:20 +00:00
// Skip if it isn't a pure repeated messages
// Does it start with a share?
2017-11-10 05:00:50 +00:00
if ( strpos ( $body , " [share " ) > 0 ) {
2017-08-03 05:42:44 +00:00
return " " ;
2017-11-10 05:00:50 +00:00
}
2015-12-10 00:10:54 +00:00
2016-03-30 21:25:20 +00:00
// Does it end with a share?
2017-11-10 05:00:50 +00:00
if ( strlen ( $body ) > ( strrpos ( $body , " [/share] " ) + 8 )) {
2017-08-03 05:42:44 +00:00
return " " ;
2017-11-10 05:00:50 +00:00
}
2015-12-10 00:10:54 +00:00
2017-11-09 16:05:18 +00:00
$attributes = preg_replace ( " / \ [share(.*?) \ ] \ s?(.*?) \ s? \ [ \ /share \ ] \ s?/ism " , " $ 1 " , $body );
2016-03-30 21:25:20 +00:00
// Skip if there is no shared message in there
2017-11-10 05:00:50 +00:00
if ( $body == $attributes ) {
2017-08-03 05:42:44 +00:00
return false ;
2017-11-10 05:00:50 +00:00
}
2015-12-10 00:10:54 +00:00
2016-03-30 21:25:20 +00:00
$guid = " " ;
preg_match ( " /guid='(.*?)'/ism " , $attributes , $matches );
2017-11-10 05:00:50 +00:00
if ( $matches [ 1 ] != " " ) {
2016-03-30 21:25:20 +00:00
$guid = $matches [ 1 ];
2017-11-10 05:00:50 +00:00
}
2015-12-10 00:10:54 +00:00
2016-03-30 21:25:20 +00:00
preg_match ( '/guid="(.*?)"/ism' , $attributes , $matches );
2017-11-10 05:00:50 +00:00
if ( $matches [ 1 ] != " " ) {
2016-03-30 21:25:20 +00:00
$guid = $matches [ 1 ];
2017-11-10 05:00:50 +00:00
}
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
return $guid ;
2015-11-23 17:44:47 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 05:34:13 +00:00
* @ brief Cleans the body of a post if it contains picture links
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ param string $body The body
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ return string The cleaned body
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function formatPicturePost ( $body )
2017-11-09 16:05:18 +00:00
{
2018-01-27 01:01:32 +00:00
$siteinfo = BBCode :: getAttachedData ( $body );
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
if (( $siteinfo [ " type " ] == " photo " )) {
2017-11-09 16:05:18 +00:00
if ( isset ( $siteinfo [ " preview " ])) {
2016-03-30 21:25:20 +00:00
$preview = $siteinfo [ " preview " ];
2017-11-09 16:05:18 +00:00
} else {
2016-03-30 21:25:20 +00:00
$preview = $siteinfo [ " image " ];
2017-11-09 16:05:18 +00:00
}
2015-12-04 04:57:33 +00:00
2016-03-30 21:25:20 +00:00
// Is it a remote picture? Then make a smaller preview here
$preview = proxy_url ( $preview , false , PROXY_SIZE_SMALL );
2015-12-04 05:50:23 +00:00
2016-03-30 21:25:20 +00:00
// Is it a local picture? Then make it smaller here
2018-01-15 13:05:12 +00:00
$preview = str_replace ([ " -0.jpg " , " -0.png " ], [ " -2.jpg " , " -2.png " ], $preview );
$preview = str_replace ([ " -1.jpg " , " -1.png " ], [ " -2.jpg " , " -2.png " ], $preview );
2015-12-04 05:50:23 +00:00
2017-11-09 16:05:18 +00:00
if ( isset ( $siteinfo [ " url " ])) {
2016-03-30 21:25:20 +00:00
$url = $siteinfo [ " url " ];
2017-11-09 16:05:18 +00:00
} else {
2016-03-30 21:25:20 +00:00
$url = $siteinfo [ " image " ];
2017-11-09 16:05:18 +00:00
}
2015-12-04 05:50:23 +00:00
2016-03-30 21:25:20 +00:00
$body = trim ( $siteinfo [ " text " ]) . " [url] " . $url . " [/url] \n [img] " . $preview . " [/img] " ;
}
2015-12-04 05:50:23 +00:00
2016-03-30 21:25:20 +00:00
return $body ;
2015-12-04 04:57:33 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds the header elements to the XML document
2016-03-30 22:14:51 +00:00
*
2017-12-30 05:34:50 +00:00
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
* @ param string $filter The related feed filter ( activity , posts or comments )
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object header root element
2016-03-30 22:14:51 +00:00
*/
2017-12-30 05:34:50 +00:00
private static function addHeader ( $doc , $owner , $filter )
2017-11-09 16:05:18 +00:00
{
2016-03-30 21:25:20 +00:00
$a = get_app ();
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
$root = $doc -> createElementNS ( NAMESPACE_ATOM1 , 'feed' );
$doc -> appendChild ( $root );
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
$root -> setAttribute ( " xmlns:thr " , NAMESPACE_THREAD );
$root -> setAttribute ( " xmlns:georss " , NAMESPACE_GEORSS );
$root -> setAttribute ( " xmlns:activity " , NAMESPACE_ACTIVITY );
$root -> setAttribute ( " xmlns:media " , NAMESPACE_MEDIA );
$root -> setAttribute ( " xmlns:poco " , NAMESPACE_POCO );
$root -> setAttribute ( " xmlns:ostatus " , NAMESPACE_OSTATUS );
$root -> setAttribute ( " xmlns:statusnet " , NAMESPACE_STATUSNET );
2017-04-27 20:38:46 +00:00
$root -> setAttribute ( " xmlns:mastodon " , NAMESPACE_MASTODON );
2015-11-23 17:44:47 +00:00
2018-02-14 04:58:46 +00:00
$title = '' ;
2017-12-30 05:34:50 +00:00
switch ( $filter ) {
2018-01-22 14:54:13 +00:00
case 'activity' : $title = L10n :: t ( '%s\'s timeline' , $owner [ 'name' ]); break ;
case 'posts' : $title = L10n :: t ( '%s\'s posts' , $owner [ 'name' ]); break ;
case 'comments' : $title = L10n :: t ( '%s\'s comments' , $owner [ 'name' ]); break ;
2017-12-30 05:19:16 +00:00
}
2018-01-15 13:05:12 +00:00
$attributes = [ " uri " => " https://friendi.ca " , " version " => FRIENDICA_VERSION . " - " . DB_UPDATE_VERSION ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " generator " , FRIENDICA_PLATFORM , $attributes );
2017-12-30 05:19:16 +00:00
XML :: addElement ( $doc , $root , " id " , System :: baseUrl () . " /profile/ " . $owner [ " nick " ]);
XML :: addElement ( $doc , $root , " title " , $title );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " subtitle " , sprintf ( " Updates from %s on %s " , $owner [ " name " ], $a -> config [ " sitename " ]));
XML :: addElement ( $doc , $root , " logo " , $owner [ " photo " ]);
2018-01-27 02:38:34 +00:00
XML :: addElement ( $doc , $root , " updated " , DateTimeFormat :: utcNow ( DateTimeFormat :: ATOM ));
2015-11-23 17:44:47 +00:00
2017-11-16 04:09:11 +00:00
$author = self :: addAuthor ( $doc , $owner );
2016-03-30 21:25:20 +00:00
$root -> appendChild ( $author );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " href " => $owner [ " url " ], " rel " => " alternate " , " type " => " text/html " ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
/// @TODO We have to find out what this is
2017-08-26 07:32:10 +00:00
/// $attributes = array("href" => System::baseUrl()."/sup",
2016-03-30 21:25:20 +00:00
/// "rel" => "http://api.friendfeed.com/2008/03#sup",
/// "type" => "application/json");
2017-11-20 17:56:31 +00:00
/// XML::addElement($doc, $root, "link", "", $attributes);
2015-11-23 17:44:47 +00:00
2017-08-23 05:01:15 +00:00
self :: hublinks ( $doc , $root , $owner [ " nick " ]);
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " href " => System :: baseUrl () . " /salmon/ " . $owner [ " nick " ], " rel " => " salmon " ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " href " => System :: baseUrl () . " /salmon/ " . $owner [ " nick " ], " rel " => " http://salmon-protocol.org/ns/salmon-replies " ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " href " => System :: baseUrl () . " /salmon/ " . $owner [ " nick " ], " rel " => " http://salmon-protocol.org/ns/salmon-mention " ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " href " => System :: baseUrl () . " /api/statuses/user_timeline/ " . $owner [ " nick " ] . " .atom " ,
" rel " => " self " , " type " => " application/atom+xml " ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2018-03-18 10:31:12 +00:00
if ( $owner [ 'account-type' ] == ACCOUNT_TYPE_COMMUNITY ) {
$condition = [ 'uid' => $owner [ 'uid' ], 'self' => false , 'pending' => false ,
'archive' => false , 'hidden' => false , 'blocked' => false ];
$members = dba :: count ( 'contact' , $condition );
XML :: addElement ( $doc , $root , " statusnet:group_info " , " " , [ " member_count " => $members ]);
}
2016-03-30 21:25:20 +00:00
return $root ;
}
2015-11-23 17:44:47 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Add the link to the push hubs to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
2016-03-31 20:01:56 +00:00
* @ param object $root XML root element where the hub links are added
2017-11-09 16:05:18 +00:00
* @ param object $nick nick
2017-11-23 19:01:58 +00:00
* @ return void
2016-03-30 22:14:51 +00:00
*/
2017-11-09 16:05:18 +00:00
public static function hublinks ( $doc , $root , $nick )
{
2017-08-26 07:32:10 +00:00
$h = System :: baseUrl () . '/pubsubhubbub/' . $nick ;
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $root , " link " , " " , [ " href " => $h , " rel " => " hub " ]);
2015-11-23 17:44:47 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2018-03-25 00:03:55 +00:00
* @ brief Adds attachment data to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
2016-03-31 20:01:56 +00:00
* @ param object $root XML root element where the hub links are added
2017-11-09 16:05:18 +00:00
* @ param array $item Data of the item that is to be posted
2017-11-23 19:01:58 +00:00
* @ return void
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function getAttachment ( $doc , $root , $item )
2017-11-09 16:05:18 +00:00
{
2016-03-30 21:25:20 +00:00
$o = " " ;
2018-01-27 01:01:32 +00:00
$siteinfo = BBCode :: getAttachedData ( $item [ " body " ]);
2016-03-30 21:25:20 +00:00
2017-04-20 05:17:00 +00:00
switch ( $siteinfo [ " type " ]) {
2016-03-30 21:25:20 +00:00
case 'photo' :
2017-12-07 13:56:11 +00:00
$imgdata = Image :: getInfoFromURL ( $siteinfo [ " image " ]);
2018-03-25 00:03:55 +00:00
if ( $imgdata ) {
$attributes = [ " rel " => " enclosure " ,
" href " => $siteinfo [ " image " ],
" type " => $imgdata [ " mime " ],
" length " => intval ( $imgdata [ " size " ])];
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
}
2016-03-30 21:25:20 +00:00
break ;
case 'video' :
2018-01-15 13:05:12 +00:00
$attributes = [ " rel " => " enclosure " ,
2016-03-30 21:25:20 +00:00
" href " => $siteinfo [ " url " ],
" type " => " text/html; charset=UTF-8 " ,
" length " => " " ,
2018-01-15 13:05:12 +00:00
" title " => $siteinfo [ " title " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2016-03-30 21:25:20 +00:00
break ;
default :
break ;
}
2015-11-23 17:44:47 +00:00
2017-08-03 05:42:44 +00:00
if ( ! Config :: get ( 'system' , 'ostatus_not_attach_preview' ) && ( $siteinfo [ " type " ] != " photo " ) && isset ( $siteinfo [ " image " ])) {
2017-12-07 13:56:11 +00:00
$imgdata = Image :: getInfoFromURL ( $siteinfo [ " image " ]);
2018-03-25 00:03:55 +00:00
if ( $imgdata ) {
$attributes = [ " rel " => " enclosure " ,
" href " => $siteinfo [ " image " ],
" type " => $imgdata [ " mime " ],
" length " => intval ( $imgdata [ " size " ])];
2015-11-27 22:16:54 +00:00
2018-03-25 00:03:55 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
}
2016-03-30 21:25:20 +00:00
}
2015-11-27 22:16:54 +00:00
2017-04-19 21:37:00 +00:00
$arr = explode ( '[/attach],' , $item [ 'attach' ]);
if ( count ( $arr )) {
foreach ( $arr as $r ) {
2016-03-30 21:25:20 +00:00
$matches = false ;
2017-04-19 21:37:00 +00:00
$cnt = preg_match ( '|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|' , $r , $matches );
if ( $cnt ) {
2018-01-15 13:05:12 +00:00
$attributes = [ " rel " => " enclosure " ,
2016-03-30 21:25:20 +00:00
" href " => $matches [ 1 ],
2018-01-15 13:05:12 +00:00
" type " => $matches [ 3 ]];
2015-11-23 17:44:47 +00:00
2017-04-19 21:37:00 +00:00
if ( intval ( $matches [ 2 ])) {
2016-03-30 21:25:20 +00:00
$attributes [ " length " ] = intval ( $matches [ 2 ]);
2017-04-19 21:37:00 +00:00
}
if ( trim ( $matches [ 4 ]) != " " ) {
2016-03-30 21:25:20 +00:00
$attributes [ " title " ] = trim ( $matches [ 4 ]);
2017-04-19 21:37:00 +00:00
}
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $root , " link " , " " , $attributes );
2016-03-30 21:25:20 +00:00
}
2015-11-23 17:44:47 +00:00
}
}
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds the author element to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object author element
2016-03-30 22:14:51 +00:00
*/
2018-03-18 10:31:12 +00:00
private static function addAuthor ( $doc , $owner , $show_profile = true )
2017-11-09 16:05:18 +00:00
{
2018-02-14 04:58:46 +00:00
$profile = dba :: selectFirst ( 'profile' , [ 'homepage' , 'publish' ], [ 'uid' => $owner [ 'uid' ], 'is-default' => true ]);
2016-03-30 21:25:20 +00:00
$author = $doc -> createElement ( " author " );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " id " , $owner [ " url " ]);
2018-03-18 10:31:12 +00:00
if ( $owner [ 'account-type' ] == ACCOUNT_TYPE_COMMUNITY ) {
XML :: addElement ( $doc , $author , " activity:object-type " , ACTIVITY_OBJ_GROUP );
} else {
XML :: addElement ( $doc , $author , " activity:object-type " , ACTIVITY_OBJ_PERSON );
}
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " uri " , $owner [ " url " ]);
XML :: addElement ( $doc , $author , " name " , $owner [ " nick " ]);
XML :: addElement ( $doc , $author , " email " , $owner [ " addr " ]);
2018-03-18 10:31:12 +00:00
if ( $show_profile ) {
XML :: addElement ( $doc , $author , " summary " , BBCode :: convert ( $owner [ " about " ], false , 7 ));
}
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " rel " => " alternate " , " type " => " text/html " , " href " => $owner [ " url " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " link " , " " , $attributes );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [
2015-12-12 09:41:42 +00:00
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
2016-03-30 21:25:20 +00:00
" media:width " => 175 ,
" media:height " => 175 ,
2018-01-15 13:05:12 +00:00
" href " => $owner [ " photo " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " link " , " " , $attributes );
2016-03-30 21:25:20 +00:00
if ( isset ( $owner [ " thumb " ])) {
2018-01-15 13:05:12 +00:00
$attributes = [
2016-03-30 21:25:20 +00:00
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
" media:width " => 80 ,
" media:height " => 80 ,
2018-01-15 13:05:12 +00:00
" href " => $owner [ " thumb " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " link " , " " , $attributes );
2016-03-30 21:25:20 +00:00
}
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $author , " poco:preferredUsername " , $owner [ " nick " ]);
XML :: addElement ( $doc , $author , " poco:displayName " , $owner [ " name " ]);
2018-03-18 10:31:12 +00:00
if ( $show_profile ) {
XML :: addElement ( $doc , $author , " poco:note " , BBCode :: convert ( $owner [ " about " ], false , 7 ));
2016-03-30 21:25:20 +00:00
2018-03-18 10:31:12 +00:00
if ( trim ( $owner [ " location " ]) != " " ) {
$element = $doc -> createElement ( " poco:address " );
XML :: addElement ( $doc , $element , " poco:formatted " , $owner [ " location " ]);
$author -> appendChild ( $element );
}
2016-03-30 21:25:20 +00:00
}
2018-03-18 10:31:12 +00:00
if ( DBM :: is_result ( $profile ) && ! $show_profile ) {
2018-02-14 04:58:46 +00:00
if ( trim ( $profile [ " homepage " ]) != " " ) {
$urls = $doc -> createElement ( " poco:urls " );
XML :: addElement ( $doc , $urls , " poco:type " , " homepage " );
XML :: addElement ( $doc , $urls , " poco:value " , $profile [ " homepage " ]);
XML :: addElement ( $doc , $urls , " poco:primary " , " true " );
$author -> appendChild ( $urls );
}
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $author , " followers " , " " , [ " url " => System :: baseUrl () . " /viewcontacts/ " . $owner [ " nick " ]]);
XML :: addElement ( $doc , $author , " statusnet:profile_info " , " " , [ " local_id " => $owner [ " uid " ]]);
2015-11-23 17:44:47 +00:00
2018-03-18 10:31:12 +00:00
if ( $profile [ " publish " ]) {
XML :: addElement ( $doc , $author , " mastodon:scope " , " public " );
}
2017-04-27 20:38:46 +00:00
}
2018-03-18 10:31:12 +00:00
2016-03-30 21:25:20 +00:00
return $author ;
2015-11-23 17:44:47 +00:00
}
2016-03-30 21:25:20 +00:00
/**
* @ TODO Picture attachments should look like this :
* < a href = " https://status.pirati.ca/attachment/572819 " title = " https://status.pirati.ca/file/heluecht-20151202T222602-rd3u49p.gif "
* class = " attachment thumbnail " id = " attachment-572819 " rel = " nofollow external " > https :// status . pirati . ca / attachment / 572819 </ a >
2017-11-09 16:05:18 +00:00
*/
2016-03-30 21:25:20 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Returns the given activity if present - otherwise returns the " post " activity
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ param array $item Data of the item that is to be posted
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return string activity
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function constructVerb ( $item )
2017-11-09 16:05:18 +00:00
{
2017-11-10 05:00:50 +00:00
if ( $item [ 'verb' ]) {
2016-03-30 21:25:20 +00:00
return $item [ 'verb' ];
2017-11-10 05:00:50 +00:00
}
2016-03-30 21:25:20 +00:00
return ACTIVITY_POST ;
2015-11-23 17:44:47 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Returns the given object type if present - otherwise returns the " note " object type
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ param array $item Data of the item that is to be posted
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return string Object type
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function constructObjecttype ( $item )
2017-11-09 16:05:18 +00:00
{
2018-01-15 13:05:12 +00:00
if ( in_array ( $item [ 'object-type' ], [ ACTIVITY_OBJ_NOTE , ACTIVITY_OBJ_COMMENT ]))
2016-03-30 21:25:20 +00:00
return $item [ 'object-type' ];
return ACTIVITY_OBJ_NOTE ;
2015-12-12 09:41:42 +00:00
}
2015-11-23 17:44:47 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds an entry element to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
* @ param bool $toplevel optional default false
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object Entry element
2016-03-30 22:14:51 +00:00
*/
2017-11-09 16:05:18 +00:00
private static function entry ( $doc , $item , $owner , $toplevel = false )
{
2018-02-14 04:58:46 +00:00
$xml = null ;
2017-11-16 04:09:11 +00:00
$repeated_guid = self :: getResharedGuid ( $item );
2017-11-10 05:00:50 +00:00
if ( $repeated_guid != " " ) {
2017-11-16 04:09:11 +00:00
$xml = self :: reshareEntry ( $doc , $item , $owner , $repeated_guid , $toplevel );
2017-11-10 05:00:50 +00:00
}
2015-11-23 17:44:47 +00:00
2017-11-10 05:00:50 +00:00
if ( $xml ) {
2016-03-30 21:25:20 +00:00
return $xml ;
2017-11-10 05:00:50 +00:00
}
2015-12-03 13:38:04 +00:00
2016-12-18 17:10:38 +00:00
if ( $item [ " verb " ] == ACTIVITY_LIKE ) {
2017-11-16 04:09:11 +00:00
return self :: likeEntry ( $doc , $item , $owner , $toplevel );
2018-01-15 13:05:12 +00:00
} elseif ( in_array ( $item [ " verb " ], [ ACTIVITY_FOLLOW , NAMESPACE_OSTATUS . " /unfollow " ])) {
2017-11-16 04:09:11 +00:00
return self :: followEntry ( $doc , $item , $owner , $toplevel );
2016-12-18 17:10:38 +00:00
} else {
2017-11-16 04:09:11 +00:00
return self :: noteEntry ( $doc , $item , $owner , $toplevel );
2016-12-18 17:10:38 +00:00
}
2016-03-30 21:25:20 +00:00
}
2015-11-23 17:44:47 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds a source entry to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $contact Array of the contact that is added
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object Source element
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function sourceEntry ( $doc , $contact )
2017-11-09 16:05:18 +00:00
{
2016-03-30 21:25:20 +00:00
$source = $doc -> createElement ( " source " );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $source , " id " , $contact [ " poll " ]);
XML :: addElement ( $doc , $source , " title " , $contact [ " name " ]);
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $source , " link " , " " , [ " rel " => " alternate " , " type " => " text/html " , " href " => $contact [ " alias " ]]);
XML :: addElement ( $doc , $source , " link " , " " , [ " rel " => " self " , " type " => " application/atom+xml " , " href " => $contact [ " poll " ]]);
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $source , " icon " , $contact [ " photo " ]);
2018-01-27 02:38:34 +00:00
XML :: addElement ( $doc , $source , " updated " , DateTimeFormat :: utc ( $contact [ " success_update " ] . " +00:00 " , DateTimeFormat :: ATOM ));
2016-03-30 21:25:20 +00:00
return $source ;
2016-02-05 20:31:11 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Fetches contact data from the contact or the gcontact table
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param string $url URL of the contact
* @ param array $owner Contact data of the poster
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return array Contact array
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function contactEntry ( $url , $owner )
2017-11-10 05:00:50 +00:00
{
2017-11-09 16:05:18 +00:00
$r = q (
" SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1 " ,
dbesc ( normalise_link ( $url )),
intval ( $owner [ " uid " ])
);
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $r )) {
2016-03-30 21:25:20 +00:00
$contact = $r [ 0 ];
$contact [ " uid " ] = - 1 ;
2017-03-21 16:02:59 +00:00
}
2017-11-08 03:57:46 +00:00
if ( ! DBM :: is_result ( $r )) {
2017-11-09 16:05:18 +00:00
$r = q (
" SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1 " ,
dbesc ( normalise_link ( $url ))
);
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $r )) {
2016-03-30 21:25:20 +00:00
$contact = $r [ 0 ];
$contact [ " uid " ] = - 1 ;
$contact [ " success_update " ] = $contact [ " updated " ];
2015-12-12 09:41:42 +00:00
}
}
2015-11-23 17:44:47 +00:00
2017-11-09 16:05:18 +00:00
if ( ! DBM :: is_result ( $r )) {
2017-03-21 16:02:59 +00:00
$contact = owner ;
2017-11-09 16:05:18 +00:00
}
2016-01-06 21:23:12 +00:00
2016-03-30 21:25:20 +00:00
if ( ! isset ( $contact [ " poll " ])) {
2017-11-07 02:22:52 +00:00
$data = Probe :: uri ( $url );
2016-03-30 21:25:20 +00:00
$contact [ " poll " ] = $data [ " poll " ];
2016-04-02 12:10:40 +00:00
2017-11-09 16:05:18 +00:00
if ( ! $contact [ " alias " ]) {
2016-04-02 12:10:40 +00:00
$contact [ " alias " ] = $data [ " alias " ];
2017-11-09 16:05:18 +00:00
}
2015-12-07 20:52:42 +00:00
}
2017-11-09 16:05:18 +00:00
if ( ! isset ( $contact [ " alias " ])) {
2016-03-30 21:25:20 +00:00
$contact [ " alias " ] = $contact [ " url " ];
2017-11-09 16:05:18 +00:00
}
2016-03-30 21:25:20 +00:00
return $contact ;
2015-12-10 17:16:16 +00:00
}
2015-12-07 20:52:42 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds an entry element with reshared content
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
2017-11-23 19:01:58 +00:00
* @ param string $repeated_guid guid
2017-11-09 16:05:18 +00:00
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object Entry element
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function reshareEntry ( $doc , $item , $owner , $repeated_guid , $toplevel )
2017-11-09 16:05:18 +00:00
{
2017-06-08 02:00:59 +00:00
if (( $item [ " id " ] != $item [ " parent " ]) && ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
2016-03-30 21:25:20 +00:00
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
}
2018-03-18 10:31:12 +00:00
$title = self :: entryHeader ( $doc , $entry , $owner , $item , $toplevel );
2016-03-30 21:25:20 +00:00
2017-11-09 16:05:18 +00:00
$r = q (
" SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1 " ,
intval ( $owner [ " uid " ]),
dbesc ( $repeated_guid ),
dbesc ( NETWORK_DFRN ),
dbesc ( NETWORK_DIASPORA ),
dbesc ( NETWORK_OSTATUS )
);
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $r )) {
2017-03-21 16:02:59 +00:00
$repeated_item = $r [ 0 ];
2017-08-03 05:42:44 +00:00
} else {
2016-03-30 21:25:20 +00:00
return false ;
2017-08-03 05:42:44 +00:00
}
2017-11-16 04:09:11 +00:00
$contact = self :: contactEntry ( $repeated_item [ 'author-link' ], $owner );
2015-12-13 13:10:18 +00:00
2015-11-23 17:44:47 +00:00
$parent_item = (( $item [ 'thr-parent' ]) ? $item [ 'thr-parent' ] : $item [ 'parent-uri' ]);
2016-03-30 21:25:20 +00:00
$title = $owner [ " nick " ] . " repeated a notice by " . $contact [ " nick " ];
2015-11-23 17:44:47 +00:00
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $entry , $item , $owner , $title , ACTIVITY_SHARE , false );
2015-12-13 13:10:18 +00:00
2016-03-30 21:25:20 +00:00
$as_object = $doc -> createElement ( " activity:object " );
2015-12-13 13:10:18 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $as_object , " activity:object-type " , NAMESPACE_ACTIVITY_SCHEMA . " activity " );
2016-03-30 21:25:20 +00:00
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $as_object , $repeated_item , $owner , " " , " " , false );
2016-03-30 21:25:20 +00:00
2018-03-18 10:31:12 +00:00
$author = self :: addAuthor ( $doc , $contact , false );
2017-04-27 20:38:46 +00:00
$as_object -> appendChild ( $author );
2016-03-30 21:25:20 +00:00
$as_object2 = $doc -> createElement ( " activity:object " );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $as_object2 , " activity:object-type " , self :: constructObjecttype ( $repeated_item ));
2016-03-30 21:25:20 +00:00
$title = sprintf ( " New comment by %s " , $contact [ " nick " ]);
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $as_object2 , $repeated_item , $owner , $title );
2016-03-30 21:25:20 +00:00
$as_object -> appendChild ( $as_object2 );
2017-11-16 04:09:11 +00:00
self :: entryFooter ( $doc , $as_object , $item , $owner , false );
2016-03-30 21:25:20 +00:00
2017-11-16 04:09:11 +00:00
$source = self :: sourceEntry ( $doc , $contact );
2016-03-30 21:25:20 +00:00
$as_object -> appendChild ( $source );
$entry -> appendChild ( $as_object );
2017-11-16 04:09:11 +00:00
self :: entryFooter ( $doc , $entry , $item , $owner );
2016-03-30 21:25:20 +00:00
return $entry ;
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds an entry element with a " like "
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object Entry element with " like "
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function likeEntry ( $doc , $item , $owner , $toplevel )
2017-11-09 16:05:18 +00:00
{
2017-06-08 02:00:59 +00:00
if (( $item [ " id " ] != $item [ " parent " ]) && ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
2016-03-30 21:25:20 +00:00
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
2015-12-13 13:10:18 +00:00
}
2016-03-30 21:25:20 +00:00
2018-03-18 10:31:12 +00:00
$title = self :: entryHeader ( $doc , $entry , $owner , $item , $toplevel );
2016-03-30 21:25:20 +00:00
$verb = NAMESPACE_ACTIVITY_SCHEMA . " favorite " ;
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $entry , $item , $owner , " Favorite " , $verb , false );
2016-03-30 21:25:20 +00:00
$as_object = $doc -> createElement ( " activity:object " );
2017-11-09 16:05:18 +00:00
$parent = q (
" SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d " ,
dbesc ( $item [ " thr-parent " ]),
intval ( $item [ " uid " ])
);
2016-03-30 21:25:20 +00:00
$parent_item = (( $item [ 'thr-parent' ]) ? $item [ 'thr-parent' ] : $item [ 'parent-uri' ]);
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $as_object , " activity:object-type " , self :: constructObjecttype ( $parent [ 0 ]));
2016-03-30 21:25:20 +00:00
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $as_object , $parent [ 0 ], $owner , " New entry " );
2016-03-30 21:25:20 +00:00
$entry -> appendChild ( $as_object );
2017-11-16 04:09:11 +00:00
self :: entryFooter ( $doc , $entry , $item , $owner );
2016-03-30 21:25:20 +00:00
return $entry ;
2015-12-13 13:10:18 +00:00
}
2015-11-23 17:44:47 +00:00
2016-12-18 17:10:38 +00:00
/**
* @ brief Adds the person object element to the XML document
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
* @ param array $contact Contact data of the target
2016-12-18 17:10:38 +00:00
*
* @ return object author element
*/
2017-11-16 04:09:11 +00:00
private static function addPersonObject ( $doc , $owner , $contact )
2017-11-09 16:05:18 +00:00
{
2016-12-18 17:10:38 +00:00
$object = $doc -> createElement ( " activity:object " );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " activity:object-type " , ACTIVITY_OBJ_PERSON );
2016-12-18 17:10:38 +00:00
if ( $contact [ 'network' ] == NETWORK_PHANTOM ) {
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " id " , $contact [ 'url' ]);
2016-12-18 17:10:38 +00:00
return $object ;
}
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " id " , $contact [ " alias " ]);
XML :: addElement ( $doc , $object , " title " , $contact [ " nick " ]);
2016-12-18 17:10:38 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " rel " => " alternate " , " type " => " text/html " , " href " => $contact [ " url " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " link " , " " , $attributes );
2016-12-18 17:10:38 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [
2016-12-18 17:10:38 +00:00
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
" media:width " => 175 ,
" media:height " => 175 ,
2018-01-15 13:05:12 +00:00
" href " => $contact [ " photo " ]];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " link " , " " , $attributes );
2016-12-18 17:10:38 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $object , " poco:preferredUsername " , $contact [ " nick " ]);
XML :: addElement ( $doc , $object , " poco:displayName " , $contact [ " name " ]);
2016-12-18 17:10:38 +00:00
if ( trim ( $contact [ " location " ]) != " " ) {
$element = $doc -> createElement ( " poco:address " );
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $element , " poco:formatted " , $contact [ " location " ]);
2016-12-18 17:10:38 +00:00
$object -> appendChild ( $element );
}
return $object ;
}
/**
* @ brief Adds a follow / unfollow entry element
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $item Data of the follow / unfollow message
* @ param array $owner Contact data of the poster
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-12-18 17:10:38 +00:00
*
* @ return object Entry element
*/
2017-11-16 04:09:11 +00:00
private static function followEntry ( $doc , $item , $owner , $toplevel )
2017-11-10 05:00:50 +00:00
{
2016-12-18 17:10:38 +00:00
$item [ " id " ] = $item [ " parent " ] = 0 ;
2016-12-18 20:02:13 +00:00
$item [ " created " ] = $item [ " edited " ] = date ( " c " );
$item [ " private " ] = true ;
2016-12-18 17:10:38 +00:00
$contact = Probe :: uri ( $item [ 'follow' ]);
if ( $contact [ 'alias' ] == '' ) {
$contact [ 'alias' ] = $contact [ " url " ];
} else {
$item [ 'follow' ] = $contact [ 'alias' ];
}
2017-11-09 16:05:18 +00:00
$r = q (
" SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' " ,
intval ( $owner [ 'uid' ]),
dbesc ( normalise_link ( $contact [ " url " ]))
);
2016-12-18 20:02:13 +00:00
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $r )) {
2016-12-18 20:02:13 +00:00
$connect_id = $r [ 0 ][ 'id' ];
} else {
$connect_id = 0 ;
}
2016-12-18 17:10:38 +00:00
if ( $item [ 'verb' ] == ACTIVITY_FOLLOW ) {
2018-01-22 14:54:13 +00:00
$message = L10n :: t ( '%s is now following %s.' );
$title = L10n :: t ( 'following' );
2016-12-18 20:02:13 +00:00
$action = " subscription " ;
2016-12-18 17:10:38 +00:00
} else {
2018-01-22 14:54:13 +00:00
$message = L10n :: t ( '%s stopped following %s.' );
$title = L10n :: t ( 'stopped following' );
2016-12-18 20:02:13 +00:00
$action = " unfollow " ;
2016-12-18 17:10:38 +00:00
}
2017-11-09 16:05:18 +00:00
$item [ " uri " ] = $item [ 'parent-uri' ] = $item [ 'thr-parent' ]
= 'tag:' . get_app () -> get_hostname () .
2016-12-18 20:02:13 +00:00
',' . date ( 'Y-m-d' ) . ':' . $action . ':' . $owner [ 'uid' ] .
':person:' . $connect_id . ':' . $item [ 'created' ];
2016-12-18 17:10:38 +00:00
2016-12-18 20:02:13 +00:00
$item [ " body " ] = sprintf ( $message , $owner [ " nick " ], $contact [ " nick " ]);
2016-12-18 17:10:38 +00:00
2018-03-18 10:31:12 +00:00
self :: entryHeader ( $doc , $entry , $owner , $item , $toplevel );
2016-12-18 20:02:13 +00:00
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $entry , $item , $owner , $title );
2016-12-18 17:10:38 +00:00
2017-11-16 04:09:11 +00:00
$object = self :: addPersonObject ( $doc , $owner , $contact );
2016-12-18 17:10:38 +00:00
$entry -> appendChild ( $object );
2017-11-16 04:09:11 +00:00
self :: entryFooter ( $doc , $entry , $item , $owner );
2016-12-18 17:10:38 +00:00
return $entry ;
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds a regular entry element
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return object Entry element
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function noteEntry ( $doc , $item , $owner , $toplevel )
2017-11-09 16:05:18 +00:00
{
2017-06-08 02:00:59 +00:00
if (( $item [ " id " ] != $item [ " parent " ]) && ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
2016-03-30 21:25:20 +00:00
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
}
2018-03-18 10:31:12 +00:00
$title = self :: entryHeader ( $doc , $entry , $owner , $item , $toplevel );
2016-03-30 21:25:20 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " activity:object-type " , ACTIVITY_OBJ_NOTE );
2015-11-23 17:44:47 +00:00
2017-11-16 04:09:11 +00:00
self :: entryContent ( $doc , $entry , $item , $owner , $title );
2015-11-23 17:44:47 +00:00
2017-11-16 04:09:11 +00:00
self :: entryFooter ( $doc , $entry , $item , $owner );
2015-12-13 13:10:18 +00:00
2016-03-30 21:25:20 +00:00
return $entry ;
2015-12-13 13:10:18 +00:00
}
2016-03-30 21:25:20 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds a header element to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param object $entry The entry element where the elements are added
* @ param array $owner Contact data of the poster
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return string The title for the element
2016-03-30 22:14:51 +00:00
*/
2018-03-18 10:31:12 +00:00
private static function entryHeader ( $doc , & $entry , $owner , $item , $toplevel )
2017-11-09 16:05:18 +00:00
{
2016-03-31 20:01:56 +00:00
/// @todo Check if this title stuff is really needed (I guess not)
2016-03-30 21:25:20 +00:00
if ( ! $toplevel ) {
$entry = $doc -> createElement ( " entry " );
$title = sprintf ( " New note by %s " , $owner [ " nick " ]);
2018-03-18 10:31:12 +00:00
if ( $owner [ 'account-type' ] == ACCOUNT_TYPE_COMMUNITY ) {
$contact = self :: contactEntry ( $item [ 'author-link' ], $owner );
$author = self :: addAuthor ( $doc , $contact , false );
$entry -> appendChild ( $author );
}
2016-03-30 21:25:20 +00:00
} else {
$entry = $doc -> createElementNS ( NAMESPACE_ATOM1 , " entry " );
$entry -> setAttribute ( " xmlns:thr " , NAMESPACE_THREAD );
$entry -> setAttribute ( " xmlns:georss " , NAMESPACE_GEORSS );
$entry -> setAttribute ( " xmlns:activity " , NAMESPACE_ACTIVITY );
$entry -> setAttribute ( " xmlns:media " , NAMESPACE_MEDIA );
$entry -> setAttribute ( " xmlns:poco " , NAMESPACE_POCO );
$entry -> setAttribute ( " xmlns:ostatus " , NAMESPACE_OSTATUS );
$entry -> setAttribute ( " xmlns:statusnet " , NAMESPACE_STATUSNET );
2017-04-27 20:38:46 +00:00
$entry -> setAttribute ( " xmlns:mastodon " , NAMESPACE_MASTODON );
2016-03-30 21:25:20 +00:00
2017-11-16 04:09:11 +00:00
$author = self :: addAuthor ( $doc , $owner );
2016-03-30 21:25:20 +00:00
$entry -> appendChild ( $author );
$title = sprintf ( " New comment by %s " , $owner [ " nick " ]);
}
return $title ;
2015-12-13 13:10:18 +00:00
}
2015-11-23 17:44:47 +00:00
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:01:56 +00:00
* @ brief Adds elements to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param object $entry Entry element where the content is added
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
* @ param string $title Title for the post
* @ param string $verb The activity verb
* @ param bool $complete Add the " status_net " element ?
2017-11-23 19:01:58 +00:00
* @ return void
2016-03-30 22:14:51 +00:00
*/
2017-11-16 04:09:11 +00:00
private static function entryContent ( $doc , $entry , $item , $owner , $title , $verb = " " , $complete = true )
2017-11-09 16:05:18 +00:00
{
if ( $verb == " " ) {
2017-11-16 04:09:11 +00:00
$verb = self :: constructVerb ( $item );
2017-11-09 16:05:18 +00:00
}
2015-11-23 17:44:47 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " id " , $item [ " uri " ]);
XML :: addElement ( $doc , $entry , " title " , $title );
2015-11-23 17:44:47 +00:00
2017-11-16 04:09:11 +00:00
$body = self :: formatPicturePost ( $item [ 'body' ]);
2015-11-23 17:44:47 +00:00
2017-11-10 05:00:50 +00:00
if ( $item [ 'title' ] != " " ) {
2016-03-30 21:25:20 +00:00
$body = " [b] " . $item [ 'title' ] . " [/b] \n \n " . $body ;
2017-11-10 05:00:50 +00:00
}
2015-11-23 17:44:47 +00:00
2018-02-15 02:33:55 +00:00
$body = BBCode :: convert ( $body , false , 7 );
2015-11-23 17:44:47 +00:00
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " content " , $body , [ " type " => " html " ]);
2015-12-07 20:52:42 +00:00
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " link " , " " , [ " rel " => " alternate " , " type " => " text/html " ,
" href " => System :: baseUrl () . " /display/ " . $item [ " guid " ]]
2017-11-09 16:05:18 +00:00
);
2015-12-07 20:52:42 +00:00
2017-11-09 16:05:18 +00:00
if ( $complete && ( $item [ " id " ] > 0 )) {
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " status_net " , " " , [ " notice_id " => $item [ " id " ]]);
2017-11-09 16:05:18 +00:00
}
2015-11-23 17:44:47 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " activity:verb " , $verb );
2015-11-23 17:44:47 +00:00
2018-01-27 02:38:34 +00:00
XML :: addElement ( $doc , $entry , " published " , DateTimeFormat :: utc ( $item [ " created " ] . " +00:00 " , DateTimeFormat :: ATOM ));
XML :: addElement ( $doc , $entry , " updated " , DateTimeFormat :: utc ( $item [ " edited " ] . " +00:00 " , DateTimeFormat :: ATOM ));
2015-11-23 17:44:47 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 20:24:54 +00:00
* @ brief Adds the elements at the foot of an entry to the XML document
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param object $doc XML document
* @ param object $entry The entry element where the elements are added
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
* @ param bool $complete default true
2017-11-23 19:01:58 +00:00
* @ return void
2016-03-30 22:14:51 +00:00
*/
2018-05-10 12:48:27 +00:00
private static function entryFooter ( $doc , $entry , array $item , array $owner , $complete = true )
2017-11-09 16:05:18 +00:00
{
2018-01-15 13:05:12 +00:00
$mentioned = [];
2016-03-30 21:25:20 +00:00
2017-06-08 02:00:59 +00:00
if (( $item [ 'parent' ] != $item [ 'id' ]) || ( $item [ 'parent-uri' ] !== $item [ 'uri' ]) || (( $item [ 'thr-parent' ] !== '' ) && ( $item [ 'thr-parent' ] !== $item [ 'uri' ]))) {
2016-03-30 21:25:20 +00:00
$parent = q ( " SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `id` = %d " , intval ( $item [ " parent " ]));
$parent_item = (( $item [ 'thr-parent' ]) ? $item [ 'thr-parent' ] : $item [ 'parent-uri' ]);
2017-11-09 16:05:18 +00:00
$thrparent = q (
" SELECT `guid`, `author-link`, `owner-link`, `plink` FROM `item` WHERE `uid` = %d AND `uri` = '%s' " ,
intval ( $owner [ " uid " ]),
dbesc ( $parent_item )
);
2017-04-27 20:38:46 +00:00
if ( $thrparent ) {
$mentioned [ $thrparent [ 0 ][ " author-link " ]] = $thrparent [ 0 ][ " author-link " ];
$mentioned [ $thrparent [ 0 ][ " owner-link " ]] = $thrparent [ 0 ][ " owner-link " ];
$parent_plink = $thrparent [ 0 ][ " plink " ];
} else {
$mentioned [ $parent [ 0 ][ " author-link " ]] = $parent [ 0 ][ " author-link " ];
$mentioned [ $parent [ 0 ][ " owner-link " ]] = $parent [ 0 ][ " owner-link " ];
2017-08-26 07:32:10 +00:00
$parent_plink = System :: baseUrl () . " /display/ " . $parent [ 0 ][ " guid " ];
2017-04-27 20:38:46 +00:00
}
2018-01-15 13:05:12 +00:00
$attributes = [
2016-03-30 21:25:20 +00:00
" ref " => $parent_item ,
2018-01-15 13:05:12 +00:00
" href " => $parent_plink ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " thr:in-reply-to " , " " , $attributes );
2016-03-30 21:25:20 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [
2016-03-30 21:25:20 +00:00
" rel " => " related " ,
2018-01-15 13:05:12 +00:00
" href " => $parent_plink ];
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " link " , " " , $attributes );
2017-04-27 20:38:46 +00:00
}
2016-03-30 21:25:20 +00:00
2017-04-27 20:38:46 +00:00
if ( intval ( $item [ " parent " ]) > 0 ) {
2017-08-26 07:32:10 +00:00
$conversation_href = System :: baseUrl () . " /display/ " . $owner [ " nick " ] . " / " . $item [ " parent " ];
2017-04-27 20:38:46 +00:00
$conversation_uri = $conversation_href ;
2016-03-30 21:25:20 +00:00
2017-04-27 20:38:46 +00:00
if ( isset ( $parent_item )) {
$r = dba :: fetch_first ( " SELECT `conversation-uri`, `conversation-href` FROM `conversation` WHERE `item-uri` = ? " , $parent_item );
2018-05-10 12:48:27 +00:00
2017-11-08 03:57:46 +00:00
if ( DBM :: is_result ( $r )) {
2017-04-27 20:38:46 +00:00
if ( $r [ 'conversation-uri' ] != '' ) {
$conversation_uri = $r [ 'conversation-uri' ];
}
if ( $r [ 'conversation-href' ] != '' ) {
$conversation_href = $r [ 'conversation-href' ];
}
}
2016-03-30 21:25:20 +00:00
}
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " link " , " " , [ " rel " => " ostatus:conversation " , " href " => $conversation_href ]);
2017-04-19 21:37:00 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [
2017-04-27 20:38:46 +00:00
" href " => $conversation_href ,
2017-04-19 21:37:00 +00:00
" local_id " => $item [ " parent " ],
2018-01-15 13:05:12 +00:00
" ref " => $conversation_uri ];
2017-04-19 21:37:00 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " ostatus:conversation " , $conversation_uri , $attributes );
2016-12-18 17:10:38 +00:00
}
2018-01-28 11:18:08 +00:00
$tags = item :: getFeedTags ( $item );
2016-03-30 21:25:20 +00:00
2017-11-09 16:05:18 +00:00
if ( count ( $tags )) {
foreach ( $tags as $t ) {
if ( $t [ 0 ] == " @ " ) {
2016-03-30 21:25:20 +00:00
$mentioned [ $t [ 1 ]] = $t [ 1 ];
2017-11-09 16:05:18 +00:00
}
}
}
2016-03-30 21:25:20 +00:00
// Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS)
2018-01-15 13:05:12 +00:00
$newmentions = [];
2017-11-09 16:05:18 +00:00
foreach ( $mentioned as $mention ) {
2016-03-30 21:25:20 +00:00
$newmentions [ str_replace ( " http:// " , " https:// " , $mention )] = str_replace ( " http:// " , " https:// " , $mention );
$newmentions [ str_replace ( " https:// " , " http:// " , $mention )] = str_replace ( " https:// " , " http:// " , $mention );
}
$mentioned = $newmentions ;
2017-11-09 16:05:18 +00:00
foreach ( $mentioned as $mention ) {
2018-03-18 11:18:25 +00:00
$condition = [ 'uid' => $owner [ 'uid' ], 'nurl' => normalise_link ( $mention )];
$contact = dba :: selectFirst ( 'contact' , [ 'forum' , 'prv' , 'self' , 'contact-type' ], $condition );
if ( $contact [ " forum " ] || $contact [ " prv " ] || ( $owner [ 'contact-type' ] == ACCOUNT_TYPE_COMMUNITY ) ||
( $contact [ 'self' ] && ( $owner [ 'account-type' ] == ACCOUNT_TYPE_COMMUNITY ))) {
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " link " , " " ,
2018-01-15 13:05:12 +00:00
[
2017-11-09 16:05:18 +00:00
" rel " => " mentioned " ,
" ostatus:object-type " => ACTIVITY_OBJ_GROUP ,
2018-01-15 13:05:12 +00:00
" href " => $mention ]
2017-11-09 16:05:18 +00:00
);
} else {
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " link " , " " ,
2018-01-15 13:05:12 +00:00
[
2017-11-09 16:05:18 +00:00
" rel " => " mentioned " ,
" ostatus:object-type " => ACTIVITY_OBJ_PERSON ,
2018-01-15 13:05:12 +00:00
" href " => $mention ]
2017-11-09 16:05:18 +00:00
);
}
2016-03-30 21:25:20 +00:00
}
2015-11-23 17:44:47 +00:00
2018-03-18 11:18:25 +00:00
if ( $owner [ 'account-type' ] == ACCOUNT_TYPE_COMMUNITY ) {
2018-05-10 12:48:27 +00:00
XML :: addElement ( $doc , $entry , " link " , " " , [
" rel " => " mentioned " ,
" ostatus:object-type " => " http://activitystrea.ms/schema/1.0/group " ,
" href " => $owner [ 'url' ]
]);
2018-03-18 11:18:25 +00:00
}
2016-03-30 21:25:20 +00:00
if ( ! $item [ " private " ]) {
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " link " , " " , [ " rel " => " ostatus:attention " ,
" href " => " http://activityschema.org/collection/public " ]);
XML :: addElement ( $doc , $entry , " link " , " " , [ " rel " => " mentioned " ,
2016-03-30 21:25:20 +00:00
" ostatus:object-type " => " http://activitystrea.ms/schema/1.0/collection " ,
2018-01-15 13:05:12 +00:00
" href " => " http://activityschema.org/collection/public " ]);
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " mastodon:scope " , " public " );
2016-03-30 21:25:20 +00:00
}
2017-11-09 16:05:18 +00:00
if ( count ( $tags )) {
foreach ( $tags as $t ) {
if ( $t [ 0 ] != " @ " ) {
2018-01-15 13:05:12 +00:00
XML :: addElement ( $doc , $entry , " category " , " " , [ " term " => $t [ 2 ]]);
2017-11-09 16:05:18 +00:00
}
}
}
2016-03-30 21:25:20 +00:00
2017-11-16 04:09:11 +00:00
self :: getAttachment ( $doc , $entry , $item );
2016-03-30 21:25:20 +00:00
2017-06-08 02:00:59 +00:00
if ( $complete && ( $item [ " id " ] > 0 )) {
2016-03-30 21:25:20 +00:00
$app = $item [ " app " ];
2017-11-10 05:00:50 +00:00
if ( $app == " " ) {
2016-03-30 21:25:20 +00:00
$app = " web " ;
2017-11-10 05:00:50 +00:00
}
2016-03-30 21:25:20 +00:00
2018-01-15 13:05:12 +00:00
$attributes = [ " local_id " => $item [ " id " ], " source " => $app ];
2016-03-30 21:25:20 +00:00
2017-11-09 16:05:18 +00:00
if ( isset ( $parent [ " id " ])) {
2016-03-30 21:25:20 +00:00
$attributes [ " repeat_of " ] = $parent [ " id " ];
2017-11-09 16:05:18 +00:00
}
2016-03-30 21:25:20 +00:00
2017-11-09 16:05:18 +00:00
if ( $item [ " coord " ] != " " ) {
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " georss:point " , $item [ " coord " ]);
2017-11-09 16:05:18 +00:00
}
2016-03-30 21:25:20 +00:00
2017-11-20 17:56:31 +00:00
XML :: addElement ( $doc , $entry , " statusnet:notice_info " , " " , $attributes );
2016-03-30 21:25:20 +00:00
}
}
2015-11-23 17:44:47 +00:00
2016-03-30 22:14:51 +00:00
/**
2017-12-30 05:19:16 +00:00
* Creates the XML feed for a given nickname
*
* Supported filters :
* - activity ( default ) : all the public posts
* - posts : all the public top - level posts
* - comments : all the public replies
*
* Updates the provided last_update parameter if the result comes from the
* cache or it is empty
*
2016-03-31 20:01:56 +00:00
* @ brief Creates the XML feed for a given nickname
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param string $owner_nick Nickname of the feed owner
* @ param string $last_update Date of the last update
* @ param integer $max_items Number of maximum items to fetch
2017-12-30 05:34:50 +00:00
* @ param string $filter Feed items filter ( activity , posts or comments )
* @ param boolean $nocache Wether to bypass caching
2016-03-30 22:14:51 +00:00
*
2016-03-31 20:01:56 +00:00
* @ return string XML feed
2016-03-30 22:14:51 +00:00
*/
2017-12-30 05:34:50 +00:00
public static function feed ( $owner_nick , & $last_update , $max_items = 300 , $filter = 'activity' , $nocache = false )
2017-11-09 16:05:18 +00:00
{
2017-08-03 05:42:44 +00:00
$stamp = microtime ( true );
2017-12-30 05:34:50 +00:00
$cachekey = " ostatus:feed: " . $owner_nick . " : " . $filter . " : " . $last_update ;
2017-08-03 05:42:44 +00:00
$previous_created = $last_update ;
$result = Cache :: get ( $cachekey );
2017-12-30 05:19:16 +00:00
if ( ! $nocache && ! is_null ( $result )) {
2017-12-30 05:34:50 +00:00
logger ( 'Feed duration: ' . number_format ( microtime ( true ) - $stamp , 3 ) . ' - ' . $owner_nick . ' - ' . $filter . ' - ' . $previous_created . ' (cached)' , LOGGER_DEBUG );
2017-08-03 05:42:44 +00:00
$last_update = $result [ 'last_update' ];
return $result [ 'feed' ];
}
2015-11-23 17:44:47 +00:00
2017-12-30 05:19:16 +00:00
$owner = dba :: fetch_first (
2018-03-11 14:01:40 +00:00
" SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`, `user`.`account-type`
2016-03-30 21:25:20 +00:00
FROM `contact` INNER JOIN `user` ON `user` . `uid` = `contact` . `uid`
2017-12-30 05:19:16 +00:00
WHERE `contact` . `self` AND `user` . `nickname` = ? LIMIT 1 " ,
$owner_nick
2017-11-09 16:05:18 +00:00
);
2017-12-30 05:19:16 +00:00
if ( ! DBM :: is_result ( $owner )) {
2016-03-30 21:25:20 +00:00
return ;
2017-08-03 05:42:44 +00:00
}
2016-03-30 21:25:20 +00:00
2017-08-03 05:42:44 +00:00
if ( ! strlen ( $last_update )) {
2016-03-30 21:25:20 +00:00
$last_update = 'now -30 days' ;
2017-08-03 05:42:44 +00:00
}
2016-03-30 21:25:20 +00:00
2018-01-27 02:38:34 +00:00
$check_date = DateTimeFormat :: utc ( $last_update );
2018-03-02 00:54:45 +00:00
$authorid = Contact :: getIdForURL ( $owner [ " url " ], 0 , true );
2016-10-21 18:25:21 +00:00
2017-12-30 05:19:16 +00:00
$sql_extra = '' ;
2017-12-30 05:34:50 +00:00
if ( $filter === 'posts' ) {
2017-12-30 05:19:16 +00:00
$sql_extra .= ' AND `item`.`id` = `item`.`parent` ' ;
}
2017-12-30 05:34:50 +00:00
if ( $filter === 'comments' ) {
2017-12-30 05:19:16 +00:00
$sql_extra .= sprintf ( " AND `item`.`object-type` = '%s' " , dbesc ( ACTIVITY_OBJ_COMMENT ));
}
2018-03-11 14:01:40 +00:00
if ( $owner [ 'account-type' ] != ACCOUNT_TYPE_COMMUNITY ) {
$sql_extra .= sprintf ( " AND `item`.`contact-id` = %d AND `item`.`author-id` = %d " , intval ( $owner [ " id " ]), intval ( $authorid ));
}
2017-11-09 16:05:18 +00:00
$items = q (
" SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`)
2016-10-21 18:25:21 +00:00
STRAIGHT_JOIN `thread` ON `thread` . `iid` = `item` . `parent`
2017-12-30 05:19:16 +00:00
WHERE `item` . `uid` = % d
AND `item` . `created` > '%s'
AND NOT `item` . `deleted`
AND NOT `item` . `private`
2018-02-18 17:29:14 +00:00
AND `item` . `visible`
2018-03-11 18:12:38 +00:00
AND `item` . `wall`
2017-12-30 05:19:16 +00:00
AND `thread` . `network` IN ( '%s' , '%s' )
$sql_extra
2017-08-23 23:01:37 +00:00
ORDER BY `item` . `created` DESC LIMIT % d " ,
2017-12-30 05:19:16 +00:00
intval ( $owner [ " uid " ]),
dbesc ( $check_date ),
dbesc ( NETWORK_OSTATUS ),
dbesc ( NETWORK_DFRN ),
intval ( $max_items )
2017-11-09 16:05:18 +00:00
);
2016-10-23 21:59:40 +00:00
2016-03-30 21:25:20 +00:00
$doc = new DOMDocument ( '1.0' , 'utf-8' );
$doc -> formatOutput = true ;
2017-12-30 05:34:50 +00:00
$root = self :: addHeader ( $doc , $owner , $filter );
2016-03-30 21:25:20 +00:00
2017-11-09 16:05:18 +00:00
foreach ( $items as $item ) {
2017-05-05 19:41:41 +00:00
if ( Config :: get ( 'system' , 'ostatus_debug' )) {
$item [ 'body' ] .= '🍼' ;
}
2016-03-30 21:25:20 +00:00
$entry = self :: entry ( $doc , $item , $owner );
$root -> appendChild ( $entry );
2017-08-03 05:42:44 +00:00
if ( $last_update < $item [ 'created' ]) {
$last_update = $item [ 'created' ];
}
2016-03-30 21:25:20 +00:00
}
2017-08-03 05:42:44 +00:00
$feeddata = trim ( $doc -> saveXML ());
2018-01-15 13:05:12 +00:00
$msg = [ 'feed' => $feeddata , 'last_update' => $last_update ];
2017-08-03 05:42:44 +00:00
Cache :: set ( $cachekey , $msg , CACHE_QUARTER_HOUR );
2017-12-30 05:34:50 +00:00
logger ( 'Feed duration: ' . number_format ( microtime ( true ) - $stamp , 3 ) . ' - ' . $owner_nick . ' - ' . $filter . ' - ' . $previous_created , LOGGER_DEBUG );
2017-08-03 05:42:44 +00:00
return $feeddata ;
2016-03-30 21:25:20 +00:00
}
2016-03-30 22:14:51 +00:00
/**
2016-03-31 05:34:13 +00:00
* @ brief Creates the XML for a salmon message
2016-03-30 22:14:51 +00:00
*
2017-11-09 16:05:18 +00:00
* @ param array $item Data of the item that is to be posted
2016-03-31 05:34:13 +00:00
* @ param array $owner Contact data of the poster
2016-03-30 22:14:51 +00:00
*
2016-03-31 05:34:13 +00:00
* @ return string XML for the salmon
2016-03-30 22:14:51 +00:00
*/
2017-11-09 16:05:18 +00:00
public static function salmon ( $item , $owner )
{
2016-03-30 21:25:20 +00:00
$doc = new DOMDocument ( '1.0' , 'utf-8' );
$doc -> formatOutput = true ;
2015-11-23 17:44:47 +00:00
2017-05-05 19:41:41 +00:00
if ( Config :: get ( 'system' , 'ostatus_debug' )) {
$item [ 'body' ] .= '🐟' ;
}
2016-03-30 21:25:20 +00:00
$entry = self :: entry ( $doc , $item , $owner , true );
2015-11-23 17:44:47 +00:00
2016-03-30 21:25:20 +00:00
$doc -> appendChild ( $entry );
2017-08-03 05:42:44 +00:00
return trim ( $doc -> saveXML ());
2016-03-30 21:25:20 +00:00
}
2015-11-23 17:44:47 +00:00
}