2015-06-07 23:18:02 +02:00
< ? php
2016-03-31 00:14:51 +02:00
/**
* @ file include / ostatus . php
*/
2015-06-20 14:40:30 +02:00
require_once ( " include/Contact.php " );
require_once ( " include/threads.php " );
require_once ( " include/html2bbcode.php " );
2015-12-14 23:02:49 +01:00
require_once ( " include/bbcode.php " );
2015-06-20 14:40:30 +02:00
require_once ( " include/items.php " );
2015-06-07 23:18:02 +02:00
require_once ( " mod/share.php " );
2015-06-20 14:40:30 +02:00
require_once ( " include/enotify.php " );
require_once ( " include/socgraph.php " );
require_once ( " include/Photo.php " );
2015-08-23 22:21:15 +02:00
require_once ( " include/Scrape.php " );
require_once ( " include/follow.php " );
2015-12-07 21:52:42 +01:00
require_once ( " include/api.php " );
2015-12-04 06:50:23 +01:00
require_once ( " mod/proxy.php " );
2016-03-30 23:25:20 +02:00
require_once ( " include/xml.php " );
2015-06-20 14:40:30 +02:00
2016-03-31 00:14:51 +02:00
/**
* @ brief This class contain functions for the OStatus protocol
*
*/
2016-03-30 23:25:20 +02:00
class ostatus {
const OSTATUS_DEFAULT_POLL_INTERVAL = 30 ; // given in minutes
const OSTATUS_DEFAULT_POLL_TIMEFRAME = 1440 ; // given in minutes
const OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS = 14400 ; // given in minutes
2015-06-07 23:18:02 +02:00
2017-04-02 01:39:19 +02:00
/**
* @ brief Mix two paths together to possibly fix missing parts
*
* @ param string $avatar Path to the avatar
* @ param string $base Another path that is hopefully complete
*
* @ return string fixed avatar path
*/
2017-04-02 14:37:22 +02:00
public static function fix_avatar ( $avatar , $base ) {
2017-04-02 01:39:19 +02:00
$base_parts = parse_url ( $base );
// Remove all parts that could create a problem
unset ( $base_parts [ 'path' ]);
unset ( $base_parts [ 'query' ]);
unset ( $base_parts [ 'fragment' ]);
$avatar_parts = parse_url ( $avatar );
// Now we mix them
$parts = array_merge ( $base_parts , $avatar_parts );
// And put them together again
$scheme = isset ( $parts [ 'scheme' ]) ? $parts [ 'scheme' ] . '://' : '' ;
$host = isset ( $parts [ 'host' ]) ? $parts [ 'host' ] : '' ;
$port = isset ( $parts [ 'port' ]) ? ':' . $parts [ 'port' ] : '' ;
$path = isset ( $parts [ 'path' ]) ? $parts [ 'path' ] : '' ;
$query = isset ( $parts [ 'query' ]) ? '?' . $parts [ 'query' ] : '' ;
$fragment = isset ( $parts [ 'fragment' ]) ? '#' . $parts [ 'fragment' ] : '' ;
2017-04-02 14:37:22 +02:00
$fixed = $scheme . $host . $port . $path . $query . $fragment ;
logger ( 'Base: ' . $base . ' - Avatar: ' . $avatar . ' - Fixed: ' . $fixed , LOGGER_DATA );
return $fixed ;
2017-04-02 01:39:19 +02:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Fetches author data
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $xpath The xpath object
* @ param object $context The xml context of the author detals
* @ 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
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ return array Array of author related entries for the item
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function fetchauthor ( $xpath , $context , $importer , & $contact , $onlyfetch ) {
2015-08-23 22:21:15 +02:00
2016-03-30 23:25:20 +02:00
$author = array ();
$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 ;
2015-08-23 22:21:15 +02:00
2016-04-02 00:24:56 +02:00
$aliaslink = $author [ " author-link " ];
2015-08-23 22:21:15 +02:00
2016-03-30 23:25:20 +02:00
$alternate = $xpath -> query ( " atom:author/atom:link[@rel='alternate'] " , $context ) -> item ( 0 ) -> attributes ;
2017-03-21 17:02:59 +01:00
if ( is_object ( $alternate ))
foreach ( $alternate AS $attributes )
if ( $attributes -> name == " href " )
2016-03-30 23:25:20 +02:00
$author [ " author-link " ] = $attributes -> textContent ;
2015-08-23 22:21:15 +02:00
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s' " ,
intval ( $importer [ " uid " ]), dbesc ( normalise_link ( $author [ " author-link " ])),
2016-04-02 00:24:56 +02:00
dbesc ( normalise_link ( $aliaslink )), dbesc ( NETWORK_STATUSNET ));
2017-03-21 17:02:59 +01:00
if ( $r ) {
2016-03-30 23:25:20 +02:00
$contact = $r [ 0 ];
$author [ " contact-id " ] = $r [ 0 ][ " id " ];
2017-03-21 17:02:59 +01:00
} else
2016-03-30 23:25:20 +02:00
$author [ " contact-id " ] = $contact [ " id " ];
2015-08-23 22:21:15 +02:00
2016-03-30 23:25:20 +02:00
$avatarlist = array ();
$avatars = $xpath -> query ( " atom:author/atom:link[@rel='avatar'] " , $context );
2017-03-21 17:02:59 +01:00
foreach ( $avatars AS $avatar ) {
2016-03-30 23:25:20 +02:00
$href = " " ;
$width = 0 ;
2017-03-21 17:02:59 +01:00
foreach ( $avatar -> attributes AS $attributes ) {
if ( $attributes -> name == " href " )
2016-03-30 23:25:20 +02:00
$href = $attributes -> textContent ;
2017-03-21 17:02:59 +01:00
if ( $attributes -> name == " width " )
2016-03-30 23:25:20 +02:00
$width = $attributes -> textContent ;
}
2017-03-21 17:02:59 +01:00
if (( $width > 0 ) AND ( $href != " " ))
2016-03-30 23:25:20 +02:00
$avatarlist [ $width ] = $href ;
2015-08-23 22:21:15 +02:00
}
2016-03-30 23:25:20 +02:00
if ( count ( $avatarlist ) > 0 ) {
krsort ( $avatarlist );
2017-04-02 01:39:19 +02:00
$author [ " author-avatar " ] = self :: fix_avatar ( current ( $avatarlist ), $author [ " author-link " ]);
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
$displayname = $xpath -> evaluate ( 'atom:author/poco:displayName/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $displayname != " " )
2016-03-30 23:25:20 +02:00
$author [ " author-name " ] = $displayname ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$author [ " owner-name " ] = $author [ " author-name " ];
$author [ " owner-link " ] = $author [ " author-link " ];
$author [ " owner-avatar " ] = $author [ " author-avatar " ];
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// Only update the contacts if it is an OStatus contact
2017-03-21 17:02:59 +01:00
if ( $r AND ! $onlyfetch AND ( $contact [ " network " ] == NETWORK_OSTATUS )) {
2016-04-02 14:10:40 +02:00
2016-03-30 23:25:20 +02:00
// Update contact data
2015-06-13 09:11:17 +02:00
2016-04-02 14:10:40 +02: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;
2016-01-08 00:35:46 +01:00
2016-04-02 14:10:40 +02:00
$value = $xpath -> evaluate ( 'atom:author/atom:uri/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $value != " " )
2016-03-30 23:25:20 +02:00
$contact [ " alias " ] = $value ;
2016-01-08 00:35:46 +01:00
2016-03-30 23:25:20 +02:00
$value = $xpath -> evaluate ( 'atom:author/poco:displayName/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $value != " " )
2016-03-30 23:25:20 +02:00
$contact [ " name " ] = $value ;
2016-01-05 15:34:27 +01:00
2016-03-30 23:25:20 +02:00
$value = $xpath -> evaluate ( 'atom:author/poco:preferredUsername/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $value != " " )
2016-03-30 23:25:20 +02:00
$contact [ " nick " ] = $value ;
2016-01-05 15:34:27 +01:00
2016-03-30 23:25:20 +02:00
$value = $xpath -> evaluate ( 'atom:author/poco:note/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $value != " " )
2016-03-30 23:25:20 +02:00
$contact [ " about " ] = html2bbcode ( $value );
2015-06-13 09:11:17 +02:00
2016-03-30 23:25:20 +02:00
$value = $xpath -> evaluate ( 'atom:author/poco:address/poco:formatted/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-03-21 17:02:59 +01:00
if ( $value != " " )
2016-03-30 23:25:20 +02:00
$contact [ " location " ] = $value ;
2015-06-13 09:11:17 +02:00
2016-04-02 00:24:56 +02:00
if (( $contact [ " name " ] != $r [ 0 ][ " name " ]) OR ( $contact [ " nick " ] != $r [ 0 ][ " nick " ]) OR ( $contact [ " about " ] != $r [ 0 ][ " about " ]) OR
( $contact [ " alias " ] != $r [ 0 ][ " alias " ]) OR ( $contact [ " location " ] != $r [ 0 ][ " location " ])) {
2015-06-13 09:11:17 +02:00
2016-03-30 23:25:20 +02:00
logger ( " Update contact data for contact " . $contact [ " id " ], LOGGER_DEBUG );
2015-06-13 09:11:17 +02:00
2016-04-02 00:24:56 +02:00
q ( " UPDATE `contact` SET `name` = '%s', `nick` = '%s', `alias` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d " ,
dbesc ( $contact [ " name " ]), dbesc ( $contact [ " nick " ]), dbesc ( $contact [ " alias " ]),
dbesc ( $contact [ " about " ]), dbesc ( $contact [ " location " ]),
2016-03-30 23:25:20 +02:00
dbesc ( datetime_convert ()), intval ( $contact [ " id " ]));
}
if ( isset ( $author [ " author-avatar " ]) AND ( $author [ " author-avatar " ] != $r [ 0 ][ 'avatar' ])) {
logger ( " Update profile picture for contact " . $contact [ " id " ], LOGGER_DEBUG );
2015-06-13 09:11:17 +02:00
2016-03-30 23:25:20 +02:00
update_contact_avatar ( $author [ " author-avatar " ], $importer [ " uid " ], $contact [ " id " ]);
}
2015-06-13 09:11:17 +02:00
2016-04-02 14:10:40 +02:00
// Ensure that we are having this contact (with uid=0)
$cid = get_contact ( $author [ " author-link " ], 0 );
if ( $cid ) {
// Update it with the current values
q ( " UPDATE `contact` SET `url` = '%s', `name` = '%s', `nick` = '%s', `alias` = '%s',
2016-04-02 15:49:57 +02:00
`about` = '%s' , `location` = '%s' ,
2016-04-02 14:10:40 +02:00
`success_update` = '%s' , `last-update` = '%s'
WHERE `id` = % d " ,
dbesc ( $author [ " author-link " ]), dbesc ( $contact [ " name " ]), dbesc ( $contact [ " nick " ]),
dbesc ( $contact [ " alias " ]), dbesc ( $contact [ " about " ]), dbesc ( $contact [ " location " ]),
dbesc ( datetime_convert ()), dbesc ( datetime_convert ()), intval ( $cid ));
// Update the avatar
update_contact_avatar ( $author [ " author-avatar " ], 0 , $cid );
}
2016-03-30 23:25:20 +02:00
$contact [ " generation " ] = 2 ;
2016-05-04 23:21:30 +02:00
$contact [ " hide " ] = false ; // OStatus contacts are never hidden
2016-03-30 23:25:20 +02:00
$contact [ " photo " ] = $author [ " author-avatar " ];
2017-03-22 06:26:44 +01:00
$gcid = update_gcontact ( $contact );
link_gcontact ( $gcid , $contact [ " uid " ], $contact [ " id " ]);
2015-06-13 09:11:17 +02:00
}
2016-01-05 15:34:27 +01:00
2016-03-30 23:25:20 +02:00
return ( $author );
2015-06-13 09:11:17 +02:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Fetches author data from a given XML string
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ param string $xml The XML
2016-03-31 07:34:13 +02:00
* @ param array $importer user record of the importing user
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return array Array of author related entries for the item
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function salmon_author ( $xml , $importer ) {
2015-07-13 08:06:51 +02:00
2017-03-21 17:02:59 +01:00
if ( $xml == " " )
2016-03-30 23:25:20 +02:00
return ;
2015-07-13 08:06:51 +02:00
2016-03-30 23:25:20 +02:00
$doc = new DOMDocument ();
2017-03-21 17:02:59 +01:00
@ $doc -> loadXML ( $xml );
2015-07-13 08:06:51 +02:00
2016-03-30 23:25:20 +02:00
$xpath = new DomXPath ( $doc );
$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 );
2015-07-13 08:06:51 +02:00
2016-03-30 23:25:20 +02:00
$entries = $xpath -> query ( '/atom:entry' );
2015-07-13 08:06:51 +02:00
2016-03-30 23:25:20 +02:00
foreach ( $entries AS $entry ) {
// fetch the author
$author = self :: fetchauthor ( $xpath , $entry , $importer , $contact , true );
return $author ;
}
2015-07-13 08:06:51 +02:00
}
2015-06-09 08:27:04 +02:00
2017-04-02 21:18:11 +02:00
/**
* @ brief Read attributes from element
*
* @ param object $element Element object
*
* @ return array attributes
*/
private static function read_attributes ( $element ) {
$attribute = array ();
foreach ( $element -> attributes AS $attributes ) {
$attribute [ $attributes -> name ] = $attributes -> textContent ;
}
return $attribute ;
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Imports an XML string containing OStatus elements
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param string $xml The XML
* @ param array $importer user record of the importing user
2016-03-31 00:14:51 +02:00
* @ param $contact
2016-03-31 07:34:13 +02:00
* @ param array $hub Called by reference , returns the fetched hub data
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function import ( $xml , $importer , & $contact , & $hub ) {
2016-03-31 22:24:54 +02:00
/// @todo this function is too long. It has to be split in many parts
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
logger ( " Import OStatus message " , LOGGER_DEBUG );
2015-06-07 23:18:02 +02:00
2017-04-02 21:18:11 +02:00
if ( $xml == " " ) {
2016-03-30 23:25:20 +02:00
return ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
//$tempfile = tempnam(get_temppath(), "import");
//file_put_contents($tempfile, $xml);
$doc = new DOMDocument ();
2017-03-21 17:02:59 +01:00
@ $doc -> loadXML ( $xml );
2016-03-30 23:25:20 +02:00
$xpath = new DomXPath ( $doc );
$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 );
$gub = " " ;
$hub_attributes = $xpath -> query ( " /atom:feed/atom:link[@rel='hub'] " ) -> item ( 0 ) -> attributes ;
2017-04-02 21:18:11 +02:00
if ( is_object ( $hub_attributes )) {
foreach ( $hub_attributes AS $hub_attribute ) {
2016-03-30 23:25:20 +02:00
if ( $hub_attribute -> name == " href " ) {
$hub = $hub_attribute -> textContent ;
logger ( " Found hub " . $hub , LOGGER_DEBUG );
}
2017-04-02 21:18:11 +02:00
}
}
2016-03-30 23:25:20 +02:00
$header = array ();
$header [ " uid " ] = $importer [ " uid " ];
$header [ " network " ] = NETWORK_OSTATUS ;
$header [ " type " ] = " remote " ;
$header [ " wall " ] = 0 ;
$header [ " origin " ] = 0 ;
$header [ " gravity " ] = GRAVITY_PARENT ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// it could either be a received post or a post we fetched by ourselves
// depending on that, the first node is different
$first_child = $doc -> firstChild -> tagName ;
2015-06-09 23:53:53 +02:00
2017-04-02 21:18:11 +02:00
if ( $first_child == " feed " ) {
2016-03-30 23:25:20 +02:00
$entries = $xpath -> query ( '/atom:feed/atom:entry' );
2017-04-26 23:16:25 +02:00
$header [ " protocol " ] = PROTOCOL_OSTATUS_FEED ;
2017-04-02 21:18:11 +02:00
} else {
2016-03-30 23:25:20 +02:00
$entries = $xpath -> query ( '/atom:entry' );
2017-04-26 23:16:25 +02:00
$header [ " protocol " ] = PROTOCOL_OSTATUS_SALMON ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$conversation = " " ;
$conversationlist = array ();
$item_id = 0 ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// Reverse the order of the entries
$entrylist = array ();
2015-06-09 08:27:04 +02:00
2017-04-02 21:18:11 +02:00
foreach ( $entries AS $entry ) {
2016-03-30 23:25:20 +02:00
$entrylist [] = $entry ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
foreach ( array_reverse ( $entrylist ) AS $entry ) {
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$mention = false ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// fetch the author
2017-04-02 21:18:11 +02:00
if ( $first_child == " feed " ) {
2016-03-30 23:25:20 +02:00
$author = self :: fetchauthor ( $xpath , $doc -> firstChild , $importer , $contact , false );
2017-04-02 21:18:11 +02:00
} else {
2016-03-30 23:25:20 +02:00
$author = self :: fetchauthor ( $xpath , $entry , $importer , $contact , false );
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$value = $xpath -> evaluate ( 'atom:author/poco:preferredUsername/text()' , $context ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
if ( $value != " " ) {
2016-03-30 23:25:20 +02:00
$nickname = $value ;
2017-04-02 21:18:11 +02:00
} else {
2016-03-30 23:25:20 +02:00
$nickname = $author [ " author-name " ];
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$item = array_merge ( $header , $author );
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// Now get the item
$item [ " uri " ] = $xpath -> query ( 'atom:id/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2015-06-07 23:53:51 +02:00
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' " ,
intval ( $importer [ " uid " ]), dbesc ( $item [ " uri " ]));
2017-04-02 21:18:11 +02:00
if ( dbm :: is_result ( $r )) {
2016-03-30 23:25:20 +02:00
logger ( " Item with uri " . $item [ " uri " ] . " for user " . $importer [ " uid " ] . " already existed under id " . $r [ 0 ][ " id " ], LOGGER_DEBUG );
continue ;
}
2015-06-07 23:53:51 +02:00
2016-03-30 23:25:20 +02:00
$item [ " body " ] = add_page_info_to_body ( html2bbcode ( $xpath -> query ( 'atom:content/text()' , $entry ) -> item ( 0 ) -> nodeValue ));
$item [ " object-type " ] = $xpath -> query ( 'activity:object-type/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2017-04-10 06:57:36 +02:00
$item [ " verb " ] = $xpath -> query ( 'activity:verb/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2015-06-21 22:02:44 +02:00
2017-04-11 03:11:28 +02:00
// Mastodon Content Warning
2017-04-11 08:12:14 +02:00
if (( $item [ " verb " ] == ACTIVITY_POST ) AND $xpath -> evaluate ( 'boolean(atom:summary)' , $entry )) {
2017-04-10 06:08:14 +02:00
$clear_text = $xpath -> query ( 'atom:summary/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2017-04-11 03:11:28 +02:00
$item [ " body " ] = html2bbcode ( $clear_text ) . '[spoiler]' . $item [ " body " ] . '[/spoiler]' ;
}
2015-06-21 22:02:44 +02:00
2016-03-30 23:25:20 +02:00
if (( $item [ " object-type " ] == ACTIVITY_OBJ_BOOKMARK ) OR ( $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 ;
2017-04-02 21:18:11 +02:00
} elseif ( $item [ " object-type " ] == ACTIVITY_OBJ_QUESTION ) {
2016-03-30 23:25:20 +02:00
$item [ " title " ] = $xpath -> query ( 'atom:title/text()' , $entry ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
2017-04-26 23:16:25 +02:00
$item [ " source " ] = $xml ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
/// @TODO
/// Delete a message
2017-04-11 04:20:03 +02:00
if ( $item [ " verb " ] == " qvitter-delete-notice " || $item [ " verb " ] == ACTIVITY_DELETE ) {
2016-03-30 23:25:20 +02:00
// ignore "Delete" messages (by now)
logger ( " Ignore delete message " . print_r ( $item , true ));
continue ;
}
2015-06-30 08:08:02 +02:00
2016-03-30 23:25:20 +02:00
if ( $item [ " verb " ] == ACTIVITY_JOIN ) {
// ignore "Join" messages
logger ( " Ignore join message " . print_r ( $item , true ));
continue ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( $item [ " verb " ] == ACTIVITY_FOLLOW ) {
new_follower ( $importer , $contact , $item , $nickname );
continue ;
}
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
if ( $item [ " verb " ] == NAMESPACE_OSTATUS . " /unfollow " ) {
lose_follower ( $importer , $contact , $item , $dummy );
continue ;
}
2015-07-13 08:06:51 +02:00
2016-03-30 23:25:20 +02:00
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 ));
2015-07-15 08:16:41 +02:00
2016-03-30 23:25:20 +02:00
$item [ " verb " ] = ACTIVITY_LIKE ;
$item [ " parent-uri " ] = $orig_uri ;
$item [ " gravity " ] = GRAVITY_LIKE ;
}
2015-07-15 08:16:41 +02:00
2016-03-30 23:25:20 +02:00
if ( $item [ " verb " ] == NAMESPACE_OSTATUS . " /unfavorite " ) {
// Ignore "Unfavorite" message
logger ( " Ignore unfavorite message " . print_r ( $item , true ));
continue ;
}
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// http://activitystrea.ms/schema/1.0/rsvp-yes
2017-04-02 21:18:11 +02:00
if ( ! in_array ( $item [ " verb " ], array ( ACTIVITY_POST , ACTIVITY_LIKE , ACTIVITY_SHARE ))) {
2016-03-30 23:25:20 +02:00
logger ( " Unhandled verb " . $item [ " verb " ] . " " . print_r ( $item , true ));
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$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 ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$related = " " ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$inreplyto = $xpath -> query ( 'thr:in-reply-to' , $entry );
if ( is_object ( $inreplyto -> item ( 0 ))) {
2017-04-02 21:18:11 +02:00
foreach ( $inreplyto -> item ( 0 ) -> attributes AS $attributes ) {
if ( $attributes -> name == " ref " ) {
2016-03-30 23:25:20 +02:00
$item [ " parent-uri " ] = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
}
if ( $attributes -> name == " href " ) {
2016-03-30 23:25:20 +02:00
$related = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
}
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
$georsspoint = $xpath -> query ( 'georss:point' , $entry );
2017-04-02 21:18:11 +02:00
if ( $georsspoint ) {
2016-03-30 23:25:20 +02:00
$item [ " coord " ] = $georsspoint -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$categories = $xpath -> query ( 'atom:category' , $entry );
if ( $categories ) {
foreach ( $categories AS $category ) {
2017-04-02 21:18:11 +02:00
foreach ( $category -> attributes AS $attributes ) {
2016-03-30 23:25:20 +02:00
if ( $attributes -> name == " term " ) {
$term = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
if ( strlen ( $item [ " tag " ])) {
2016-03-30 23:25:20 +02:00
$item [ " tag " ] .= ',' ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$item [ " tag " ] .= " #[url= " . App :: get_baseurl () . " /search?tag= " . $term . " ] " . $term . " [/url] " ;
}
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
}
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
$self = " " ;
$enclosure = " " ;
$links = $xpath -> query ( 'atom:link' , $entry );
if ( $links ) {
foreach ( $links AS $link ) {
2017-04-02 21:18:11 +02:00
$attribute = self :: read_attributes ( $link );
if (( $attribute [ 'rel' ] != " " ) AND ( $attribute [ 'href' ] != " " )) {
switch ( $attribute [ 'rel' ]) {
2016-03-30 23:25:20 +02:00
case " alternate " :
2017-04-02 21:18:11 +02:00
$item [ " plink " ] = $attribute [ 'href' ];
2016-03-30 23:25:20 +02:00
if (( $item [ " object-type " ] == ACTIVITY_OBJ_QUESTION ) OR
2017-04-02 21:18:11 +02:00
( $item [ " object-type " ] == ACTIVITY_OBJ_EVENT )) {
$item [ " body " ] .= add_page_info ( $attribute [ 'href' ]);
}
2016-03-30 23:25:20 +02:00
break ;
case " ostatus:conversation " :
2017-04-02 21:18:11 +02:00
$conversation = $attribute [ 'href' ];
2016-03-30 23:25:20 +02:00
break ;
case " enclosure " :
2017-04-02 21:18:11 +02:00
$enclosure = $attribute [ 'href' ];
if ( strlen ( $item [ " attach " ])) {
2016-03-30 23:25:20 +02:00
$item [ " attach " ] .= ',' ;
2017-04-02 21:18:11 +02:00
}
if ( ! isset ( $attribute [ 'length' ])) {
$attribute [ 'length' ] = " 0 " ;
}
$item [ " attach " ] .= '[attach]href="' . $attribute [ 'href' ] . '" length="' . $attribute [ 'length' ] . '" type="' . $attribute [ 'type' ] . '" title="' . $attribute [ 'title' ] . '"[/attach]' ;
2016-03-30 23:25:20 +02:00
break ;
case " related " :
if ( $item [ " object-type " ] != ACTIVITY_OBJ_BOOKMARK ) {
2017-04-02 21:18:11 +02:00
if ( ! isset ( $item [ " parent-uri " ])) {
$item [ " parent-uri " ] = $attribute [ 'href' ];
}
if ( $related == " " ) {
$related = $attribute [ 'href' ];
}
} else {
$item [ " body " ] .= add_page_info ( $attribute [ 'href' ]);
}
2016-03-30 23:25:20 +02:00
break ;
case " self " :
2017-04-02 21:18:11 +02:00
$self = $attribute [ 'href' ];
2016-03-30 23:25:20 +02:00
break ;
case " mentioned " :
// Notification check
2017-04-02 21:18:11 +02:00
if ( $importer [ " nurl " ] == normalise_link ( $attribute [ 'href' ])) {
2016-03-30 23:25:20 +02:00
$mention = true ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
break ;
}
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
}
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
$local_id = " " ;
$repeat_of = " " ;
$notice_info = $xpath -> query ( 'statusnet:notice_info' , $entry );
if ( $notice_info AND ( $notice_info -> length > 0 )) {
2017-04-02 21:18:11 +02:00
foreach ( $notice_info -> item ( 0 ) -> attributes AS $attributes ) {
if ( $attributes -> name == " source " ) {
2016-03-30 23:25:20 +02:00
$item [ " app " ] = strip_tags ( $attributes -> textContent );
2017-04-02 21:18:11 +02:00
}
if ( $attributes -> name == " local_id " ) {
2016-03-30 23:25:20 +02:00
$local_id = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
}
if ( $attributes -> name == " repeat_of " ) {
2016-03-30 23:25:20 +02:00
$repeat_of = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
}
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
// Is it a repeated post?
2016-12-18 21:34:34 +01:00
if (( $repeat_of != " " ) OR ( $item [ " verb " ] == ACTIVITY_SHARE )) {
2016-03-30 23:25:20 +02:00
$activityobjects = $xpath -> query ( 'activity:object' , $entry ) -> item ( 0 );
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
if ( is_object ( $activityobjects )) {
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$orig_uri = $xpath -> query ( " activity:object/atom:id " , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
if ( ! isset ( $orig_uri )) {
2016-03-30 23:25:20 +02:00
$orig_uri = $xpath -> query ( 'atom:id/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$orig_links = $xpath -> query ( " activity:object/atom:link[@rel='alternate'] " , $activityobjects );
2017-04-02 21:18:11 +02:00
if ( $orig_links AND ( $orig_links -> length > 0 )) {
foreach ( $orig_links -> item ( 0 ) -> attributes AS $attributes ) {
if ( $attributes -> name == " href " ) {
2016-03-30 23:25:20 +02:00
$orig_link = $attributes -> textContent ;
2017-04-02 21:18:11 +02:00
}
}
}
if ( ! isset ( $orig_link )) {
2016-03-30 23:25:20 +02:00
$orig_link = $xpath -> query ( " atom:link[@rel='alternate'] " , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
if ( ! isset ( $orig_link )) {
2016-03-30 23:25:20 +02:00
$orig_link = self :: convert_href ( $orig_uri );
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$orig_body = $xpath -> query ( 'activity:object/atom:content/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
if ( ! isset ( $orig_body )) {
2016-03-30 23:25:20 +02:00
$orig_body = $xpath -> query ( 'atom:content/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$orig_created = $xpath -> query ( 'atom:published/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2016-05-31 21:18:14 +02:00
$orig_edited = $xpath -> query ( 'atom:updated/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$orig_contact = $contact ;
$orig_author = self :: fetchauthor ( $xpath , $activityobjects , $importer , $orig_contact , false );
2015-06-07 23:18:02 +02:00
2015-06-13 09:11:17 +02:00
$item [ " author-name " ] = $orig_author [ " author-name " ];
$item [ " author-link " ] = $orig_author [ " author-link " ];
2017-04-02 14:37:22 +02:00
$item [ " author-avatar " ] = $orig_author [ " author-avatar " ];
2017-04-02 01:39:19 +02:00
2015-06-12 00:39:56 +02:00
$item [ " body " ] = add_page_info_to_body ( html2bbcode ( $orig_body ));
$item [ " created " ] = $orig_created ;
2016-05-31 21:18:14 +02:00
$item [ " edited " ] = $orig_edited ;
2015-06-13 09:11:17 +02:00
$item [ " uri " ] = $orig_uri ;
2017-04-02 01:39:19 +02:00
if ( ! isset ( $item [ " plink " ])) {
$item [ " plink " ] = $orig_link ;
}
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$item [ " verb " ] = $xpath -> query ( 'activity:verb/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2015-06-20 17:26:47 +02:00
2016-03-30 23:25:20 +02:00
$item [ " object-type " ] = $xpath -> query ( 'activity:object/activity:object-type/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
if ( ! isset ( $item [ " object-type " ])) {
2016-03-30 23:25:20 +02:00
$item [ " object-type " ] = $xpath -> query ( 'activity:object-type/text()' , $activityobjects ) -> item ( 0 ) -> nodeValue ;
2017-04-02 21:18:11 +02:00
}
2017-04-02 22:43:54 +02:00
2017-04-02 21:18:11 +02:00
$enclosures = $xpath -> query ( " atom:link[@rel='alternate'] " , $activityobjects );
if ( $enclosures ) {
foreach ( $enclosures AS $link ) {
$attribute = self :: read_attributes ( $link );
if ( $href != " " ) {
$enclosure = $attribute [ 'href' ];
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]' ;
}
}
}
2016-03-30 23:25:20 +02:00
}
2015-06-07 23:18:02 +02:00
}
2016-03-30 23:25:20 +02:00
//if ($enclosure != "")
// $item["body"] .= add_page_info($enclosure);
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
if ( isset ( $item [ " parent-uri " ])) {
$r = q ( " SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' " ,
intval ( $importer [ " uid " ]), dbesc ( $item [ " parent-uri " ]));
2015-06-07 23:18:02 +02:00
2017-01-15 10:07:57 +01:00
// Only fetch missing stuff if it is a comment or reshare.
if ( in_array ( $item [ " verb " ], array ( ACTIVITY_POST , ACTIVITY_SHARE )) AND
! dbm :: is_result ( $r ) AND ( $related != " " )) {
2016-03-30 23:25:20 +02:00
$reply_path = str_replace ( " /notice/ " , " /api/statuses/show/ " , $related ) . " .atom " ;
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
if ( $reply_path != $related ) {
logger ( " Fetching related items for user " . $importer [ " uid " ] . " from " . $reply_path , LOGGER_DEBUG );
$reply_xml = fetch_url ( $reply_path );
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
$reply_contact = $contact ;
self :: import ( $reply_xml , $importer , $reply_contact , $reply_hub );
2015-06-07 23:18:02 +02:00
2016-03-30 23:25:20 +02:00
// After the import try to fetch the parent item again
$r = q ( " SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' " ,
intval ( $importer [ " uid " ]), dbesc ( $item [ " parent-uri " ]));
}
2015-06-07 23:18:02 +02:00
}
2017-04-02 21:18:11 +02:00
if ( dbm :: is_result ( $r )) {
2016-03-30 23:25:20 +02:00
$item [ " type " ] = 'remote-comment' ;
$item [ " gravity " ] = GRAVITY_COMMENT ;
}
2017-04-02 21:18:11 +02:00
} else {
2016-03-30 23:25:20 +02:00
$item [ " parent-uri " ] = $item [ " uri " ];
2017-04-02 21:18:11 +02:00
}
2016-03-30 23:25:20 +02:00
$item_id = self :: completion ( $conversation , $importer [ " uid " ], $item , $self );
2015-06-20 08:44:29 +02:00
2016-03-30 23:25:20 +02:00
if ( ! $item_id ) {
logger ( " Error storing item " , LOGGER_DEBUG );
continue ;
}
2015-06-07 23:53:51 +02:00
2016-03-30 23:25:20 +02:00
logger ( " Item was stored with id " . $item_id , LOGGER_DEBUG );
}
2015-06-07 23:18:02 +02:00
}
2015-06-20 14:40:30 +02:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Create an url out of an uri
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param string $href URI in the format " parameter1:parameter1:... "
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ return string URL in the format http ( s ) ://....
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function convert_href ( $href ) {
$elements = explode ( " : " , $href );
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if (( count ( $elements ) <= 2 ) OR ( $elements [ 0 ] != " tag " ))
2016-03-30 23:25:20 +02:00
return $href ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$server = explode ( " , " , $elements [ 1 ]);
$conversation = explode ( " = " , $elements [ 2 ]);
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if (( count ( $elements ) == 4 ) AND ( $elements [ 2 ] == " post " ))
2016-03-30 23:25:20 +02:00
return " http:// " . $server [ 0 ] . " /notice/ " . $elements [ 3 ];
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if (( count ( $conversation ) != 2 ) OR ( $conversation [ 1 ] == " " ))
2016-03-30 23:25:20 +02:00
return $href ;
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( $elements [ 3 ] == " objectType=thread " )
2016-03-30 23:25:20 +02:00
return " http:// " . $server [ 0 ] . " /conversation/ " . $conversation [ 1 ];
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
return " http:// " . $server [ 0 ] . " /notice/ " . $conversation [ 1 ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
return $href ;
2015-06-29 21:53:59 +02:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Checks if there are entries in conversations that aren ' t present on our side
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param bool $mentions Fetch conversations where we are mentioned
* @ param bool $override Override the interval setting
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function check_conversations ( $mentions = false , $override = false ) {
$last = get_config ( 'system' , 'ostatus_last_poll' );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$poll_interval = intval ( get_config ( 'system' , 'ostatus_poll_interval' ));
2016-12-27 19:07:57 +01:00
if ( ! $poll_interval ) {
$poll_interval = self :: OSTATUS_DEFAULT_POLL_INTERVAL ;
}
2016-03-30 23:25:20 +02:00
// Don't poll if the interval is set negative
2016-12-27 19:07:57 +01:00
if (( $poll_interval < 0 ) AND ! $override ) {
2015-06-20 14:40:30 +02:00
return ;
2016-12-27 19:07:57 +01:00
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( ! $mentions ) {
$poll_timeframe = intval ( get_config ( 'system' , 'ostatus_poll_timeframe' ));
2016-12-27 19:07:57 +01:00
if ( ! $poll_timeframe ) {
$poll_timeframe = self :: OSTATUS_DEFAULT_POLL_TIMEFRAME ;
}
2016-03-30 23:25:20 +02:00
} else {
$poll_timeframe = intval ( get_config ( 'system' , 'ostatus_poll_timeframe' ));
2016-12-27 19:07:57 +01:00
if ( ! $poll_timeframe ) {
$poll_timeframe = self :: OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS ;
}
2016-03-30 23:25:20 +02:00
}
2015-06-20 14:40:30 +02:00
2015-06-29 21:53:59 +02:00
2016-03-30 23:25:20 +02:00
if ( $last AND ! $override ) {
$next = $last + ( $poll_interval * 60 );
if ( $next > time ()) {
logger ( 'poll interval not reached' );
return ;
}
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( 'cron_start' );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$start = date ( " Y-m-d H:i:s " , time () - ( $poll_timeframe * 60 ));
2015-06-20 14:40:30 +02:00
2016-12-27 19:07:57 +01:00
if ( $mentions ) {
2016-03-30 23:25:20 +02:00
$conversations = q ( " SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term`
STRAIGHT_JOIN `thread` ON `thread` . `iid` = `term` . `oid` AND `thread` . `uid` = `term` . `uid`
WHERE `term` . `type` = 7 AND `term` . `term` > '%s' AND `thread` . `mention`
2017-04-12 00:19:05 +02:00
GROUP BY `term` . `url` , `term` . `uid` , `term` . `oid` , `term` . `term` ORDER BY `term` . `term` DESC " , dbesc( $start ));
2016-12-27 19:07:57 +01:00
} else {
2016-03-30 23:25:20 +02:00
$conversations = q ( " SELECT `oid`, `url`, `uid` FROM `term`
WHERE `type` = 7 AND `term` > '%s'
2017-04-12 01:06:08 +02:00
GROUP BY `url` , `uid` , `oid` , `term` ORDER BY `term` DESC " , dbesc( $start ));
2016-12-27 19:07:57 +01:00
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
foreach ( $conversations AS $conversation ) {
self :: completion ( $conversation [ 'url' ], $conversation [ 'uid' ]);
}
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
logger ( 'cron_end' );
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
set_config ( 'system' , 'ostatus_last_poll' , time ());
}
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
/**
* @ brief Updates the gcontact table with actor data from the conversation
*
* @ param object $actor The actor object that contains the contact data
*/
private function conv_fetch_actor ( $actor ) {
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
// We set the generation to "3" since the data here is not as reliable as the data we get on other occasions
$contact = array ( " network " => NETWORK_OSTATUS , " generation " => 3 );
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> url ))
2016-03-30 23:25:20 +02:00
$contact [ " url " ] = $actor -> url ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> displayName ))
2016-03-30 23:25:20 +02:00
$contact [ " name " ] = $actor -> displayName ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> portablecontacts_net -> displayName ))
2016-03-30 23:25:20 +02:00
$contact [ " name " ] = $actor -> portablecontacts_net -> displayName ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> portablecontacts_net -> preferredUsername ))
2016-03-30 23:25:20 +02:00
$contact [ " nick " ] = $actor -> portablecontacts_net -> preferredUsername ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> id ))
2016-03-30 23:25:20 +02:00
$contact [ " alias " ] = $actor -> id ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> summary ))
2016-03-30 23:25:20 +02:00
$contact [ " about " ] = $actor -> summary ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> portablecontacts_net -> note ))
2016-03-30 23:25:20 +02:00
$contact [ " about " ] = $actor -> portablecontacts_net -> note ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> portablecontacts_net -> addresses -> formatted ))
2016-03-30 23:25:20 +02:00
$contact [ " location " ] = $actor -> portablecontacts_net -> addresses -> formatted ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> image -> url ))
2016-03-30 23:25:20 +02:00
$contact [ " photo " ] = $actor -> image -> url ;
2016-02-13 21:35:05 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $actor -> image -> width ))
2016-03-30 23:25:20 +02:00
$avatarwidth = $actor -> image -> width ;
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
if ( is_array ( $actor -> status_net -> avatarLinks ))
foreach ( $actor -> status_net -> avatarLinks AS $avatar ) {
if ( $avatarsize < $avatar -> width ) {
$contact [ " photo " ] = $avatar -> url ;
$avatarsize = $avatar -> width ;
}
}
2016-03-06 20:36:28 +01:00
2016-05-04 23:21:30 +02:00
$contact [ " hide " ] = false ; // OStatus contacts are never hidden
2016-03-30 23:25:20 +02:00
update_gcontact ( $contact );
2016-03-06 20:36:28 +01:00
}
2016-03-30 23:25:20 +02:00
/**
* @ brief Fetches the conversation url for a given item link or conversation id
*
* @ param string $self The link to the posting
* @ param string $conversation_id The conversation id
*
* @ return string The conversation url
*/
private function fetch_conversation ( $self , $conversation_id = " " ) {
if ( $conversation_id != " " ) {
$elements = explode ( " : " , $conversation_id );
2017-03-21 17:02:59 +01:00
if (( count ( $elements ) <= 2 ) OR ( $elements [ 0 ] != " tag " ))
2016-03-30 23:25:20 +02:00
return $conversation_id ;
}
2016-03-06 20:36:28 +01:00
2017-03-21 17:02:59 +01:00
if ( $self == " " )
2016-03-30 23:25:20 +02:00
return " " ;
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
$json = str_replace ( " .atom " , " .json " , $self );
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
$raw = fetch_url ( $json );
2017-03-21 17:02:59 +01:00
if ( $raw == " " )
2016-03-30 23:25:20 +02:00
return " " ;
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
$data = json_decode ( $raw );
2017-03-21 17:02:59 +01:00
if ( ! is_object ( $data ))
2016-03-30 23:25:20 +02:00
return " " ;
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
$conversation_id = $data -> statusnet_conversation_id ;
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
$pos = strpos ( $self , " /api/statuses/show/ " );
$base_url = substr ( $self , 0 , $pos );
2016-03-06 20:36:28 +01:00
2016-03-30 23:25:20 +02:00
return $base_url . " /conversation/ " . $conversation_id ;
}
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
/**
* @ brief Fetches actor details of a given actor and user id
*
* @ param string $actor The actor url
* @ param int $uid The user id
* @ param int $contact_id The default contact - id
*
* @ return array Array with actor details
*/
private function get_actor_details ( $actor , $uid , $contact_id ) {
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
$details = array ();
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
$contact = q ( " SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s' " ,
$uid , normalise_link ( $actor ), NETWORK_STATUSNET );
2016-03-09 21:53:30 +01:00
2017-03-21 17:02:59 +01:00
if ( ! $contact )
2016-03-30 23:25:20 +02:00
$contact = q ( " SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `alias` IN ('%s', '%s') AND `network` != '%s' " ,
$uid , $actor , normalise_link ( $actor ), NETWORK_STATUSNET );
2016-03-09 21:53:30 +01:00
2017-03-21 17:02:59 +01:00
if ( $contact ) {
2016-03-30 23:25:20 +02:00
logger ( " Found contact for url " . $actor , LOGGER_DEBUG );
$details [ " contact_id " ] = $contact [ 0 ][ " id " ];
$details [ " network " ] = $contact [ 0 ][ " network " ];
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
$details [ " not_following " ] = ! in_array ( $contact [ 0 ][ " rel " ], array ( CONTACT_IS_SHARING , CONTACT_IS_FRIEND ));
} else {
logger ( " No contact found for user " . $uid . " and url " . $actor , LOGGER_DEBUG );
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
// Adding a global contact
/// @TODO Use this data for the post
$details [ " global_contact_id " ] = get_contact ( $actor , 0 );
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
logger ( " Global contact " . $global_contact_id . " found for url " . $actor , LOGGER_DEBUG );
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
$details [ " contact_id " ] = $contact_id ;
$details [ " network " ] = NETWORK_OSTATUS ;
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
$details [ " not_following " ] = true ;
}
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
return $details ;
}
2015-06-20 14:40:30 +02:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Stores an item and completes the thread
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ param string $conversation_url The URI of the conversation
* @ param integer $uid The user id
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return integer The item id of the posted item array
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function completion ( $conversation_url , $uid , $item = array (), $self = " " ) {
2015-06-26 10:41:11 +02:00
2016-03-31 22:24:54 +02:00
/// @todo This function is totally ugly and has to be rewritten totally
2015-06-20 14:40:30 +02:00
2017-03-27 07:33:43 +02:00
// Import all threads or only threads that were started by our followers?
$all_threads = ! get_config ( 'system' , 'ostatus_full_threads' );
2016-03-30 23:25:20 +02:00
$item_stored = - 1 ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$conversation_url = self :: fetch_conversation ( $self , $conversation_url );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// If the thread shouldn't be completed then store the item and go away
// Don't do a completion on liked content
if ((( intval ( get_config ( 'system' , 'ostatus_poll_interval' )) == - 2 ) AND ( count ( $item ) > 0 )) OR
( $item [ " verb " ] == ACTIVITY_LIKE ) OR ( $conversation_url == " " )) {
2017-03-27 07:33:43 +02:00
$item_stored = item_store ( $item , $all_threads );
2017-03-27 08:11:45 +02:00
return $item_stored ;
2016-03-30 23:25:20 +02:00
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Get the parent
2016-10-21 20:25:21 +02:00
$parents = q ( " SELECT `item`.`id`, `item`.`parent`, `item`.`uri`, `item`.`contact-id`, `item`.`type`,
`item` . `verb` , `item` . `visible` FROM `term`
STRAIGHT_JOIN `item` AS `thritem` ON `thritem` . `parent` = `term` . `oid`
STRAIGHT_JOIN `item` ON `item` . `parent` = `thritem` . `parent`
2016-10-24 00:12:45 +02:00
WHERE `term` . `uid` = % d AND `term` . `otype` = % d AND `term` . `type` = % d AND `term` . `url` = '%s' " ,
2016-10-21 20:25:21 +02:00
intval ( $uid ), intval ( TERM_OBJ_POST ), intval ( TERM_CONVERSATION ), dbesc ( $conversation_url ));
2016-10-23 23:59:40 +02:00
/* 2016 - 10 - 23 : The old query will be kept until we are sure that the query above is a good and fast replacement
2016-03-30 23:25:20 +02:00
$parents = q ( " SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
( SELECT `parent` FROM `item` WHERE `id` IN
( SELECT `oid` FROM `term` WHERE `uid` = % d AND `otype` = % d AND `type` = % d AND `url` = '%s' )) " ,
intval ( $uid ), intval ( TERM_OBJ_POST ), intval ( TERM_CONVERSATION ), dbesc ( $conversation_url ));
2016-10-21 20:25:21 +02:00
*/
2017-03-21 17:02:59 +01:00
if ( $parents )
2016-03-30 23:25:20 +02:00
$parent = $parents [ 0 ];
2017-03-21 17:02:59 +01:00
elseif ( count ( $item ) > 0 ) {
2016-03-30 23:25:20 +02:00
$parent = $item ;
$parent [ " type " ] = " remote " ;
$parent [ " verb " ] = ACTIVITY_POST ;
$parent [ " visible " ] = 1 ;
} else {
// Preset the parent
$r = q ( " SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d " , $uid );
2017-03-21 17:02:59 +01:00
if ( ! $r )
2016-03-30 23:25:20 +02:00
return ( - 2 );
$parent = array ();
$parent [ " id " ] = 0 ;
$parent [ " parent " ] = 0 ;
$parent [ " uri " ] = " " ;
$parent [ " contact-id " ] = $r [ 0 ][ " id " ];
$parent [ " type " ] = " remote " ;
$parent [ " verb " ] = ACTIVITY_POST ;
$parent [ " visible " ] = 1 ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$conv = str_replace ( " /conversation/ " , " /api/statusnet/conversation/ " , $conversation_url ) . " .as " ;
$pageno = 1 ;
$items = array ();
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( 'fetching conversation url ' . $conv . ' (Self: ' . $self . ') for user ' . $uid );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
do {
$conv_arr = z_fetch_url ( $conv . " ?page= " . $pageno );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// If it is a non-ssl site and there is an error, then try ssl or vice versa
if ( ! $conv_arr [ " success " ] AND ( substr ( $conv , 0 , 7 ) == " http:// " )) {
$conv = str_replace ( " http:// " , " https:// " , $conv );
$conv_as = fetch_url ( $conv . " ?page= " . $pageno );
} elseif ( ! $conv_arr [ " success " ] AND ( substr ( $conv , 0 , 8 ) == " https:// " )) {
$conv = str_replace ( " https:// " , " http:// " , $conv );
$conv_as = fetch_url ( $conv . " ?page= " . $pageno );
2017-03-21 17:02:59 +01:00
} else
2016-03-30 23:25:20 +02:00
$conv_as = $conv_arr [ " body " ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$conv_as = str_replace ( ',"statusnet:notice_info":' , ',"statusnet_notice_info":' , $conv_as );
$conv_as = json_decode ( $conv_as );
2015-11-08 11:10:20 +01:00
2016-03-30 23:25:20 +02:00
$no_of_items = sizeof ( $items );
2015-11-08 11:10:20 +01:00
2017-03-21 17:02:59 +01:00
if ( @ is_array ( $conv_as -> items ))
foreach ( $conv_as -> items AS $single_item )
2016-03-30 23:25:20 +02:00
$items [ $single_item -> id ] = $single_item ;
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( $no_of_items == sizeof ( $items ))
2016-03-30 23:25:20 +02:00
break ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$pageno ++ ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
} while ( true );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( 'fetching conversation done. Found ' . count ( $items ) . ' items' );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( ! sizeof ( $items )) {
if ( count ( $item ) > 0 ) {
2017-03-27 07:33:43 +02:00
$item_stored = item_store ( $item , $all_threads );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( $item_stored ) {
logger ( " Conversation " . $conversation_url . " couldn't be fetched. Item uri " . $item [ " uri " ] . " stored: " . $item_stored , LOGGER_DEBUG );
self :: store_conversation ( $item_id , $conversation_url );
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
return ( $item_stored );
2017-03-21 17:02:59 +01:00
} else
2016-03-30 23:25:20 +02:00
return ( - 3 );
}
$items = array_reverse ( $items );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self` " , intval ( $uid ));
$importer = $r [ 0 ];
2015-06-26 10:41:11 +02:00
2016-03-30 23:25:20 +02:00
$new_parent = true ;
2016-03-06 19:27:34 +01:00
2016-03-30 23:25:20 +02:00
foreach ( $items as $single_conv ) {
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Update the gcontact table
self :: conv_fetch_actor ( $single_conv -> actor );
2016-02-13 21:35:05 +01:00
2016-03-30 23:25:20 +02:00
// Test - remove before flight
//$tempfile = tempnam(get_temppath(), "conversation");
//file_put_contents($tempfile, json_encode($single_conv));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$mention = false ;
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> object -> id ))
2016-03-30 23:25:20 +02:00
$single_conv -> id = $single_conv -> object -> id ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$plink = self :: convert_href ( $single_conv -> id );
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> object -> url ))
2016-03-30 23:25:20 +02:00
$plink = self :: convert_href ( $single_conv -> object -> url );
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( @! $single_conv -> id )
2016-03-30 23:25:20 +02:00
continue ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( " Got id " . $single_conv -> id , LOGGER_DEBUG );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( $first_id == " " ) {
$first_id = $single_conv -> id ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// The first post of the conversation isn't our first post. There are three options:
// 1. Our conversation hasn't the "real" thread starter
// 2. This first post is a post inside our thread
// 3. This first post is a post inside another thread
if (( $first_id != $parent [ " uri " ]) AND ( $parent [ " uri " ] != " " )) {
2016-03-06 14:53:30 +01:00
2016-03-30 23:25:20 +02:00
$new_parent = true ;
2016-03-06 15:13:19 +01:00
2016-03-30 23:25:20 +02:00
$new_parents = q ( " SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
( SELECT `parent` FROM `item`
WHERE `uid` = % d AND `uri` = '%s' AND `network` IN ( '%s' , '%s' )) LIMIT 1 " ,
intval ( $uid ), dbesc ( $first_id ), dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ));
2017-03-21 17:02:59 +01:00
if ( $new_parents ) {
2016-03-30 23:25:20 +02:00
if ( $new_parents [ 0 ][ " parent " ] == $parent [ " parent " ]) {
// Option 2: This post is already present inside our thread - but not as thread starter
logger ( " Option 2: uri present in our thread: " . $first_id , LOGGER_DEBUG );
$first_id = $parent [ " uri " ];
} else {
// Option 3: Not so good. We have mixed parents. We have to see how to clean this up.
// For now just take the new parent.
$parent = $new_parents [ 0 ];
$first_id = $parent [ " uri " ];
logger ( " Option 3: mixed parents for uri " . $first_id , LOGGER_DEBUG );
}
2015-06-20 14:40:30 +02:00
} else {
2016-03-30 23:25:20 +02:00
// Option 1: We hadn't got the real thread starter
// We have to clean up our existing messages.
$parent [ " id " ] = 0 ;
$parent [ " uri " ] = $first_id ;
logger ( " Option 1: we have a new parent: " . $first_id , LOGGER_DEBUG );
2015-06-20 14:40:30 +02:00
}
2016-03-30 23:25:20 +02:00
} elseif ( $parent [ " uri " ] == " " ) {
2015-06-20 14:40:30 +02:00
$parent [ " id " ] = 0 ;
$parent [ " uri " ] = $first_id ;
}
}
2016-03-30 23:25:20 +02:00
$parent_uri = $parent [ " uri " ];
2015-06-24 22:31:38 +02:00
2016-03-30 23:25:20 +02:00
// "context" only seems to exist on older servers
if ( isset ( $single_conv -> context -> inReplyTo -> id )) {
$parent_exists = q ( " SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1 " ,
intval ( $uid ), dbesc ( $single_conv -> context -> inReplyTo -> id ), dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ));
2017-03-21 17:02:59 +01:00
if ( $parent_exists )
2016-03-30 23:25:20 +02:00
$parent_uri = $single_conv -> context -> inReplyTo -> id ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// This is the current way
if ( isset ( $single_conv -> object -> inReplyTo -> id )) {
$parent_exists = q ( " SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1 " ,
intval ( $uid ), dbesc ( $single_conv -> object -> inReplyTo -> id ), dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ));
2017-03-21 17:02:59 +01:00
if ( $parent_exists )
2016-03-30 23:25:20 +02:00
$parent_uri = $single_conv -> object -> inReplyTo -> id ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$message_exists = q ( " SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1 " ,
intval ( $uid ), dbesc ( $single_conv -> id ),
dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ));
if ( $message_exists ) {
logger ( " Message " . $single_conv -> id . " already existed on the system " , LOGGER_DEBUG );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( $parent [ " id " ] != 0 ) {
$existing_message = $message_exists [ 0 ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// We improved the way we fetch OStatus messages, this shouldn't happen very often now
/// @TODO We have to change the shadow copies as well. This way here is really ugly.
if ( $existing_message [ " parent " ] != $parent [ " id " ]) {
logger ( 'updating id ' . $existing_message [ " id " ] . ' with parent ' . $existing_message [ " parent " ] . ' to parent ' . $parent [ " id " ] . ' uri ' . $parent [ " uri " ] . ' thread ' . $parent_uri , LOGGER_DEBUG );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Update the parent id of the selected item
$r = q ( " UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d " ,
intval ( $parent [ " id " ]), dbesc ( $parent [ " uri " ]), intval ( $existing_message [ " id " ]));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Update the parent uri in the thread - but only if it points to itself
$r = q ( " UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent` " ,
dbesc ( $parent_uri ), intval ( $existing_message [ " id " ]));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// try to change all items of the same parent
$r = q ( " UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d " ,
intval ( $parent [ " id " ]), dbesc ( $parent [ " uri " ]), intval ( $existing_message [ " parent " ]));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Update the parent uri in the thread - but only if it points to itself
$r = q ( " UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`) " ,
dbesc ( $parent [ " uri " ]), intval ( $existing_message [ " parent " ]));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Now delete the thread
delete_thread ( $existing_message [ " parent " ]);
}
2015-06-20 14:40:30 +02:00
}
2016-03-30 23:25:20 +02:00
// The item we are having on the system is the one that we wanted to store via the item array
if ( isset ( $item [ " uri " ]) AND ( $item [ " uri " ] == $existing_message [ " uri " ])) {
$item = array ();
$item_stored = 0 ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
continue ;
}
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( is_array ( $single_conv -> to ))
foreach ( $single_conv -> to AS $to )
if ( $importer [ " nurl " ] == normalise_link ( $to -> id ))
2016-03-30 23:25:20 +02:00
$mention = true ;
2015-06-26 10:41:11 +02:00
2016-03-30 23:25:20 +02:00
$actor = $single_conv -> actor -> id ;
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> actor -> url ))
2016-03-30 23:25:20 +02:00
$actor = $single_conv -> actor -> url ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$details = self :: get_actor_details ( $actor , $uid , $parent [ " contact-id " ]);
2016-03-06 19:27:34 +01:00
2016-03-30 23:25:20 +02:00
// Do we only want to import threads that were started by our contacts?
if ( $details [ " not_following " ] AND $new_parent AND get_config ( 'system' , 'ostatus_full_threads' )) {
logger ( " Don't import uri " . $first_id . " because user " . $uid . " doesn't follow the person " . $actor , LOGGER_DEBUG );
continue ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$arr = array ();
$arr [ " network " ] = $details [ " network " ];
$arr [ " uri " ] = $single_conv -> id ;
$arr [ " plink " ] = $plink ;
$arr [ " uid " ] = $uid ;
$arr [ " contact-id " ] = $details [ " contact_id " ];
$arr [ " parent-uri " ] = $parent_uri ;
$arr [ " created " ] = $single_conv -> published ;
$arr [ " edited " ] = $single_conv -> published ;
$arr [ " owner-name " ] = $single_conv -> actor -> displayName ;
2017-03-21 17:02:59 +01:00
if ( $arr [ " owner-name " ] == '' )
2016-03-30 23:25:20 +02:00
$arr [ " owner-name " ] = $single_conv -> actor -> contact -> displayName ;
2017-03-21 17:02:59 +01:00
if ( $arr [ " owner-name " ] == '' )
2016-03-30 23:25:20 +02:00
$arr [ " owner-name " ] = $single_conv -> actor -> portablecontacts_net -> displayName ;
$arr [ " owner-link " ] = $actor ;
2017-04-02 14:37:22 +02:00
$arr [ " owner-avatar " ] = self :: fix_avatar ( $single_conv -> actor -> image -> url , $arr [ " owner-link " ]);
2016-03-30 23:25:20 +02:00
$arr [ " author-name " ] = $arr [ " owner-name " ];
2017-04-02 14:37:22 +02:00
$arr [ " author-link " ] = $arr [ " owner-link " ];
$arr [ " author-avatar " ] = $arr [ " owner-avatar " ];
2016-03-30 23:25:20 +02:00
$arr [ " body " ] = add_page_info_to_body ( html2bbcode ( $single_conv -> content ));
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> status_net -> notice_info -> source ))
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = strip_tags ( $single_conv -> status_net -> notice_info -> source );
2017-03-21 17:02:59 +01:00
elseif ( isset ( $single_conv -> statusnet -> notice_info -> source ))
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = strip_tags ( $single_conv -> statusnet -> notice_info -> source );
2017-03-21 17:02:59 +01:00
elseif ( isset ( $single_conv -> statusnet_notice_info -> source ))
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = strip_tags ( $single_conv -> statusnet_notice_info -> source );
2017-03-21 17:02:59 +01:00
elseif ( isset ( $single_conv -> provider -> displayName ))
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = $single_conv -> provider -> displayName ;
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = " OStatus " ;
2017-03-21 17:02:59 +01:00
2015-06-20 14:40:30 +02:00
2017-04-26 23:16:25 +02:00
$arr [ " source " ] = json_encode ( $single_conv );
$arr [ " protocol " ] = PROTOCOL_GS_CONVERSATION ;
2016-03-30 23:25:20 +02:00
$arr [ " verb " ] = $parent [ " verb " ];
$arr [ " visible " ] = $parent [ " visible " ];
$arr [ " location " ] = $single_conv -> location -> displayName ;
$arr [ " coord " ] = trim ( $single_conv -> location -> lat . " " . $single_conv -> location -> lon );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Is it a reshared item?
if ( isset ( $single_conv -> verb ) AND ( $single_conv -> verb == " share " ) AND isset ( $single_conv -> object )) {
2017-03-21 17:02:59 +01:00
if ( is_array ( $single_conv -> object ))
2016-03-30 23:25:20 +02:00
$single_conv -> object = $single_conv -> object [ 0 ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( " Found reshared item " . $single_conv -> object -> id );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// $single_conv->object->context->conversation;
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> object -> object -> id ))
2016-03-30 23:25:20 +02:00
$arr [ " uri " ] = $single_conv -> object -> object -> id ;
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$arr [ " uri " ] = $single_conv -> object -> id ;
2015-06-20 17:26:47 +02:00
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> object -> object -> url ))
2016-03-30 23:25:20 +02:00
$plink = self :: convert_href ( $single_conv -> object -> object -> url );
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$plink = self :: convert_href ( $single_conv -> object -> url );
2015-06-20 17:26:47 +02:00
2017-03-21 17:02:59 +01:00
if ( isset ( $single_conv -> object -> object -> content ))
2016-03-30 23:25:20 +02:00
$arr [ " body " ] = add_page_info_to_body ( html2bbcode ( $single_conv -> object -> object -> content ));
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$arr [ " body " ] = add_page_info_to_body ( html2bbcode ( $single_conv -> object -> content ));
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$arr [ " plink " ] = $plink ;
2015-06-20 17:26:47 +02:00
2016-03-30 23:25:20 +02:00
$arr [ " created " ] = $single_conv -> object -> published ;
$arr [ " edited " ] = $single_conv -> object -> published ;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$arr [ " author-name " ] = $single_conv -> object -> actor -> displayName ;
2017-04-02 14:37:22 +02:00
if ( $arr [ " owner-name " ] == '' ) {
2016-03-30 23:25:20 +02:00
$arr [ " author-name " ] = $single_conv -> object -> actor -> contact -> displayName ;
2017-04-02 14:37:22 +02:00
}
2016-03-30 23:25:20 +02:00
$arr [ " author-link " ] = $single_conv -> object -> actor -> url ;
2017-04-02 14:37:22 +02:00
$arr [ " author-avatar " ] = self :: fix_avatar ( $single_conv -> object -> actor -> image -> url , $arr [ " author-link " ]);
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$arr [ " app " ] = $single_conv -> object -> provider -> displayName . " # " ;
//$arr["verb"] = $single_conv->object->verb;
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$arr [ " location " ] = $single_conv -> object -> location -> displayName ;
$arr [ " coord " ] = trim ( $single_conv -> object -> location -> lat . " " . $single_conv -> object -> location -> lon );
}
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( $arr [ " location " ] == " " )
2016-03-30 23:25:20 +02:00
unset ( $arr [ " location " ]);
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( $arr [ " coord " ] == " " )
2016-03-30 23:25:20 +02:00
unset ( $arr [ " coord " ]);
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Copy fields from given item array
if ( isset ( $item [ " uri " ]) AND (( $item [ " uri " ] == $arr [ " uri " ]) OR ( $item [ " uri " ] == $single_conv -> id ))) {
$copy_fields = array ( " owner-name " , " owner-link " , " owner-avatar " , " author-name " , " author-link " , " author-avatar " ,
" gravity " , " body " , " object-type " , " object " , " verb " , " created " , " edited " , " coord " , " tag " ,
" title " , " attach " , " app " , " type " , " location " , " contact-id " , " uri " );
2017-03-21 17:02:59 +01:00
foreach ( $copy_fields AS $field )
if ( isset ( $item [ $field ]))
2017-01-26 10:07:12 +01:00
$arr [ $field ] = $item [ $field ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$newitem = item_store ( $arr );
if ( ! $newitem ) {
logger ( " Item wasn't stored " . print_r ( $arr , true ), LOGGER_DEBUG );
continue ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
if ( isset ( $item [ " uri " ]) AND ( $item [ " uri " ] == $arr [ " uri " ])) {
$item = array ();
$item_stored = $newitem ;
}
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
logger ( 'Stored new item ' . $plink . ' for parent ' . $arr [ " parent-uri " ] . ' under id ' . $newitem , LOGGER_DEBUG );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Add the conversation entry (but don't fetch the whole conversation)
self :: store_conversation ( $newitem , $conversation_url );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// If the newly created item is the top item then change the parent settings of the thread
// This shouldn't happen anymore. This is supposed to be absolote.
if ( $arr [ " uri " ] == $first_id ) {
logger ( 'setting new parent to id ' . $newitem );
$new_parents = q ( " SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1 " ,
intval ( $uid ), intval ( $newitem ));
2017-03-21 17:02:59 +01:00
if ( $new_parents )
2016-03-30 23:25:20 +02:00
$parent = $new_parents [ 0 ];
}
2015-06-20 14:40:30 +02:00
}
2016-03-30 23:25:20 +02:00
if (( $item_stored < 0 ) AND ( count ( $item ) > 0 )) {
2016-03-09 21:53:30 +01:00
2016-03-30 23:25:20 +02:00
if ( get_config ( 'system' , 'ostatus_full_threads' )) {
$details = self :: get_actor_details ( $item [ " owner-link " ], $uid , $item [ " contact-id " ]);
if ( $details [ " not_following " ]) {
logger ( " Don't import uri " . $item [ " uri " ] . " because user " . $uid . " doesn't follow the person " . $item [ " owner-link " ], LOGGER_DEBUG );
return false ;
}
2016-03-09 21:53:30 +01:00
}
2017-03-27 07:33:43 +02:00
$item_stored = item_store ( $item , $all_threads );
2016-03-30 23:25:20 +02:00
if ( $item_stored ) {
logger ( " Uri " . $item [ " uri " ] . " wasn't found in conversation " . $conversation_url , LOGGER_DEBUG );
self :: store_conversation ( $item_stored , $conversation_url );
}
2015-06-21 01:36:23 +02:00
}
2016-03-30 23:25:20 +02:00
return ( $item_stored );
}
2015-06-20 14:40:30 +02:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Stores conversation data into the database
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ param integer $itemid The id of the item
* @ param string $conversation_url The uri of the conversation
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function store_conversation ( $itemid , $conversation_url ) {
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$conversation_url = self :: convert_href ( $conversation_url );
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
$messages = q ( " SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1 " , intval ( $itemid ));
2017-03-21 17:02:59 +01:00
if ( ! $messages )
2016-03-30 23:25:20 +02:00
return ;
$message = $messages [ 0 ];
2015-06-20 14:40:30 +02:00
2016-03-30 23:25:20 +02:00
// Store conversation url if not done before
$conversation = q ( " SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d " ,
intval ( $message [ " uid " ]), intval ( $itemid ), intval ( TERM_OBJ_POST ), intval ( TERM_CONVERSATION ));
2015-06-20 14:40:30 +02:00
2017-03-21 17:02:59 +01:00
if ( ! $conversation ) {
2016-03-30 23:25:20 +02:00
$r = q ( " INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s') " ,
intval ( $message [ " uid " ]), intval ( $itemid ), intval ( TERM_OBJ_POST ), intval ( TERM_CONVERSATION ),
dbesc ( $message [ " created " ]), dbesc ( $conversation_url ), dbesc ( $message [ " created " ]), dbesc ( $message [ " received " ]), dbesc ( $message [ " guid " ]));
logger ( 'Storing conversation url ' . $conversation_url . ' for id ' . $itemid );
}
2015-06-20 14:40:30 +02:00
}
2015-12-10 01:10:54 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Checks if the current post is a reshare
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param array $item The item array of thw post
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ return string The guid if the post is a reshare
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function get_reshared_guid ( $item ) {
$body = trim ( $item [ " body " ]);
2015-12-10 01:10:54 +01:00
2016-03-30 23:25:20 +02:00
// Skip if it isn't a pure repeated messages
// Does it start with a share?
2017-03-21 17:02:59 +01:00
if ( strpos ( $body , " [share " ) > 0 )
2016-03-30 23:25:20 +02:00
return ( " " );
2015-12-10 01:10:54 +01:00
2016-03-30 23:25:20 +02:00
// Does it end with a share?
2017-03-21 17:02:59 +01:00
if ( strlen ( $body ) > ( strrpos ( $body , " [/share] " ) + 8 ))
2016-03-30 23:25:20 +02:00
return ( " " );
2015-12-10 01:10:54 +01:00
2016-03-30 23:25:20 +02:00
$attributes = preg_replace ( " / \ [share(.*?) \ ] \ s?(.*?) \ s? \ [ \ /share \ ] \ s?/ism " , " $ 1 " , $body );
// Skip if there is no shared message in there
2017-03-21 17:02:59 +01:00
if ( $body == $attributes )
2016-03-30 23:25:20 +02:00
return ( false );
2015-12-10 01:10:54 +01:00
2016-03-30 23:25:20 +02:00
$guid = " " ;
preg_match ( " /guid='(.*?)'/ism " , $attributes , $matches );
2017-03-21 17:02:59 +01:00
if ( $matches [ 1 ] != " " )
2016-03-30 23:25:20 +02:00
$guid = $matches [ 1 ];
2015-12-10 01:10:54 +01:00
2016-03-30 23:25:20 +02:00
preg_match ( '/guid="(.*?)"/ism' , $attributes , $matches );
2017-03-21 17:02:59 +01:00
if ( $matches [ 1 ] != " " )
2016-03-30 23:25:20 +02:00
$guid = $matches [ 1 ];
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
return $guid ;
2015-11-23 18:44:47 +01:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Cleans the body of a post if it contains picture links
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param string $body The body
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ return string The cleaned body
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function format_picture_post ( $body ) {
$siteinfo = get_attached_data ( $body );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
if (( $siteinfo [ " type " ] == " photo " )) {
2017-03-21 17:02:59 +01:00
if ( isset ( $siteinfo [ " preview " ]))
2016-03-30 23:25:20 +02:00
$preview = $siteinfo [ " preview " ];
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$preview = $siteinfo [ " image " ];
2015-12-04 05:57:33 +01:00
2016-03-30 23:25:20 +02:00
// Is it a remote picture? Then make a smaller preview here
$preview = proxy_url ( $preview , false , PROXY_SIZE_SMALL );
2015-12-04 06:50:23 +01:00
2016-03-30 23:25:20 +02:00
// Is it a local picture? Then make it smaller here
$preview = str_replace ( array ( " -0.jpg " , " -0.png " ), array ( " -2.jpg " , " -2.png " ), $preview );
$preview = str_replace ( array ( " -1.jpg " , " -1.png " ), array ( " -2.jpg " , " -2.png " ), $preview );
2015-12-04 06:50:23 +01:00
2017-03-21 17:02:59 +01:00
if ( isset ( $siteinfo [ " url " ]))
2016-03-30 23:25:20 +02:00
$url = $siteinfo [ " url " ];
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
$url = $siteinfo [ " image " ];
2015-12-04 06:50:23 +01:00
2016-03-30 23:25:20 +02:00
$body = trim ( $siteinfo [ " text " ]) . " [url] " . $url . " [/url] \n [img] " . $preview . " [/img] " ;
}
2015-12-04 06:50:23 +01:00
2016-03-30 23:25:20 +02:00
return $body ;
2015-12-04 05:57:33 +01:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds the header elements to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object header root element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function add_header ( $doc , $owner ) {
2015-12-04 05:57:33 +01:00
2016-03-30 23:25:20 +02:00
$a = get_app ();
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$root = $doc -> createElementNS ( NAMESPACE_ATOM1 , 'feed' );
$doc -> appendChild ( $root );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02: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 );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " uri " => " https://friendi.ca " , " version " => FRIENDICA_VERSION . " - " . DB_UPDATE_VERSION );
xml :: add_element ( $doc , $root , " generator " , FRIENDICA_PLATFORM , $attributes );
xml :: add_element ( $doc , $root , " id " , App :: get_baseurl () . " /profile/ " . $owner [ " nick " ]);
xml :: add_element ( $doc , $root , " title " , sprintf ( " %s timeline " , $owner [ " name " ]));
xml :: add_element ( $doc , $root , " subtitle " , sprintf ( " Updates from %s on %s " , $owner [ " name " ], $a -> config [ " sitename " ]));
xml :: add_element ( $doc , $root , " logo " , $owner [ " photo " ]);
xml :: add_element ( $doc , $root , " updated " , datetime_convert ( " UTC " , " UTC " , " now " , ATOM_TIME ));
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$author = self :: add_author ( $doc , $owner );
$root -> appendChild ( $author );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " href " => $owner [ " url " ], " rel " => " alternate " , " type " => " text/html " );
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
/// @TODO We have to find out what this is
/// $attributes = array("href" => App::get_baseurl()."/sup",
/// "rel" => "http://api.friendfeed.com/2008/03#sup",
/// "type" => "application/json");
/// xml::add_element($doc, $root, "link", "", $attributes);
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
self :: hublinks ( $doc , $root );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " href " => App :: get_baseurl () . " /salmon/ " . $owner [ " nick " ], " rel " => " salmon " );
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " href " => App :: get_baseurl () . " /salmon/ " . $owner [ " nick " ], " rel " => " http://salmon-protocol.org/ns/salmon-replies " );
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " href " => App :: get_baseurl () . " /salmon/ " . $owner [ " nick " ], " rel " => " http://salmon-protocol.org/ns/salmon-mention " );
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " href " => App :: get_baseurl () . " /api/statuses/user_timeline/ " . $owner [ " nick " ] . " .atom " ,
" rel " => " self " , " type " => " application/atom+xml " );
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
return $root ;
}
2015-11-23 18:44:47 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Add the link to the push hubs to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:01:56 +02:00
* @ param object $root XML root element where the hub links are added
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function hublinks ( $doc , $root ) {
$hub = get_config ( 'system' , 'huburl' );
$hubxml = '' ;
2017-03-21 17:02:59 +01:00
if ( strlen ( $hub )) {
2016-03-30 23:25:20 +02:00
$hubs = explode ( ',' , $hub );
2017-03-21 17:02:59 +01:00
if ( count ( $hubs )) {
foreach ( $hubs as $h ) {
2016-03-30 23:25:20 +02:00
$h = trim ( $h );
2017-03-21 17:02:59 +01:00
if ( ! strlen ( $h ))
2016-03-30 23:25:20 +02:00
continue ;
2017-03-21 17:02:59 +01:00
if ( $h === '[internal]' )
2016-03-30 23:25:20 +02:00
$h = App :: get_baseurl () . '/pubsubhubbub' ;
xml :: add_element ( $doc , $root , " link " , " " , array ( " href " => $h , " rel " => " hub " ));
}
2015-11-23 18:44:47 +01:00
}
}
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds attachement data to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:01:56 +02:00
* @ param object $root XML root element where the hub links are added
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function get_attachment ( $doc , $root , $item ) {
$o = " " ;
$siteinfo = get_attached_data ( $item [ " body " ]);
2017-04-20 07:17:00 +02:00
switch ( $siteinfo [ " type " ]) {
2016-03-30 23:25:20 +02:00
case 'photo' :
$imgdata = get_photo_info ( $siteinfo [ " image " ]);
$attributes = array ( " rel " => " enclosure " ,
" href " => $siteinfo [ " image " ],
" type " => $imgdata [ " mime " ],
" length " => intval ( $imgdata [ " size " ]));
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
break ;
case 'video' :
$attributes = array ( " rel " => " enclosure " ,
" href " => $siteinfo [ " url " ],
" type " => " text/html; charset=UTF-8 " ,
" length " => " " ,
" title " => $siteinfo [ " title " ]);
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
break ;
default :
break ;
}
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
if (( $siteinfo [ " type " ] != " photo " ) AND isset ( $siteinfo [ " image " ])) {
2017-04-19 23:37:00 +02:00
$imgdata = get_photo_info ( $siteinfo [ " image " ]);
$attributes = array ( " rel " => " enclosure " ,
" href " => $siteinfo [ " image " ],
" type " => $imgdata [ " mime " ],
" length " => intval ( $imgdata [ " size " ]));
2015-11-27 23:16:54 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
}
2015-11-27 23:16:54 +01:00
2017-04-19 23:37:00 +02:00
$arr = explode ( '[/attach],' , $item [ 'attach' ]);
if ( count ( $arr )) {
foreach ( $arr as $r ) {
2016-03-30 23:25:20 +02:00
$matches = false ;
2017-04-19 23:37:00 +02:00
$cnt = preg_match ( '|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|' , $r , $matches );
if ( $cnt ) {
2016-03-30 23:25:20 +02:00
$attributes = array ( " rel " => " enclosure " ,
" href " => $matches [ 1 ],
" type " => $matches [ 3 ]);
2015-11-23 18:44:47 +01:00
2017-04-19 23:37:00 +02:00
if ( intval ( $matches [ 2 ])) {
2016-03-30 23:25:20 +02:00
$attributes [ " length " ] = intval ( $matches [ 2 ]);
2017-04-19 23:37:00 +02:00
}
if ( trim ( $matches [ 4 ]) != " " ) {
2016-03-30 23:25:20 +02:00
$attributes [ " title " ] = trim ( $matches [ 4 ]);
2017-04-19 23:37:00 +02:00
}
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $root , " link " , " " , $attributes );
}
2015-11-23 18:44:47 +01:00
}
}
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds the author element to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object author element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function add_author ( $doc , $owner ) {
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT `homepage` FROM `profile` WHERE `uid` = %d AND `is-default` LIMIT 1 " , intval ( $owner [ " uid " ]));
2017-03-21 17:02:59 +01:00
if ( $r )
2016-03-30 23:25:20 +02:00
$profile = $r [ 0 ];
2015-12-20 10:44:03 +01:00
2016-03-30 23:25:20 +02:00
$author = $doc -> createElement ( " author " );
xml :: add_element ( $doc , $author , " activity:object-type " , ACTIVITY_OBJ_PERSON );
xml :: add_element ( $doc , $author , " uri " , $owner [ " url " ]);
xml :: add_element ( $doc , $author , " name " , $owner [ " name " ]);
xml :: add_element ( $doc , $author , " summary " , bbcode ( $owner [ " about " ], false , false , 7 ));
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$attributes = array ( " rel " => " alternate " , " type " => " text/html " , " href " => $owner [ " url " ]);
xml :: add_element ( $doc , $author , " link " , " " , $attributes );
2015-11-23 18:44:47 +01:00
2015-12-12 10:41:42 +01:00
$attributes = array (
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
2016-03-30 23:25:20 +02:00
" media:width " => 175 ,
" media:height " => 175 ,
" href " => $owner [ " photo " ]);
xml :: add_element ( $doc , $author , " link " , " " , $attributes );
if ( isset ( $owner [ " thumb " ])) {
$attributes = array (
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
" media:width " => 80 ,
" media:height " => 80 ,
" href " => $owner [ " thumb " ]);
xml :: add_element ( $doc , $author , " link " , " " , $attributes );
}
xml :: add_element ( $doc , $author , " poco:preferredUsername " , $owner [ " nick " ]);
xml :: add_element ( $doc , $author , " poco:displayName " , $owner [ " name " ]);
xml :: add_element ( $doc , $author , " poco:note " , bbcode ( $owner [ " about " ], false , false , 7 ));
if ( trim ( $owner [ " location " ]) != " " ) {
$element = $doc -> createElement ( " poco:address " );
xml :: add_element ( $doc , $element , " poco:formatted " , $owner [ " location " ]);
$author -> appendChild ( $element );
}
if ( trim ( $profile [ " homepage " ]) != " " ) {
$urls = $doc -> createElement ( " poco:urls " );
xml :: add_element ( $doc , $urls , " poco:type " , " homepage " );
xml :: add_element ( $doc , $urls , " poco:value " , $profile [ " homepage " ]);
xml :: add_element ( $doc , $urls , " poco:primary " , " true " );
$author -> appendChild ( $urls );
}
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
if ( count ( $profile )) {
xml :: add_element ( $doc , $author , " followers " , " " , array ( " url " => App :: get_baseurl () . " /viewcontacts/ " . $owner [ " nick " ]));
xml :: add_element ( $doc , $author , " statusnet:profile_info " , " " , array ( " local_id " => $owner [ " uid " ]));
}
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
return $author ;
2015-11-23 18:44:47 +01:00
}
2016-03-30 23:25:20 +02: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 >
*
*/
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Returns the given activity if present - otherwise returns the " post " activity
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return string activity
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
function construct_verb ( $item ) {
2017-03-21 17:02:59 +01:00
if ( $item [ 'verb' ])
2016-03-30 23:25:20 +02:00
return $item [ 'verb' ];
return ACTIVITY_POST ;
2015-11-23 18:44:47 +01:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Returns the given object type if present - otherwise returns the " note " object type
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return string Object type
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
function construct_objecttype ( $item ) {
2017-03-21 17:02:59 +01:00
if ( in_array ( $item [ 'object-type' ], array ( ACTIVITY_OBJ_NOTE , ACTIVITY_OBJ_COMMENT )))
2016-03-30 23:25:20 +02:00
return $item [ 'object-type' ];
return ACTIVITY_OBJ_NOTE ;
2015-12-12 10:41:42 +01:00
}
2015-11-23 18:44:47 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds an entry element to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02: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
2016-03-31 22:01:56 +02:00
* @ param bool $toplevel
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object Entry element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function entry ( $doc , $item , $owner , $toplevel = false ) {
$repeated_guid = self :: get_reshared_guid ( $item );
if ( $repeated_guid != " " )
$xml = self :: reshare_entry ( $doc , $item , $owner , $repeated_guid , $toplevel );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
if ( $xml )
return $xml ;
2015-12-03 14:38:04 +01:00
2016-12-18 18:10:38 +01:00
if ( $item [ " verb " ] == ACTIVITY_LIKE ) {
2016-03-30 23:25:20 +02:00
return self :: like_entry ( $doc , $item , $owner , $toplevel );
2016-12-18 18:10:38 +01:00
} elseif ( in_array ( $item [ " verb " ], array ( ACTIVITY_FOLLOW , NAMESPACE_OSTATUS . " /unfollow " ))) {
return self :: follow_entry ( $doc , $item , $owner , $toplevel );
} else {
2016-03-30 23:25:20 +02:00
return self :: note_entry ( $doc , $item , $owner , $toplevel );
2016-12-18 18:10:38 +01:00
}
2016-03-30 23:25:20 +02:00
}
2015-11-23 18:44:47 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds a source entry to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:01:56 +02:00
* @ param array $contact Array of the contact that is added
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object Source element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function source_entry ( $doc , $contact ) {
$source = $doc -> createElement ( " source " );
xml :: add_element ( $doc , $source , " id " , $contact [ " poll " ]);
xml :: add_element ( $doc , $source , " title " , $contact [ " name " ]);
xml :: add_element ( $doc , $source , " link " , " " , array ( " rel " => " alternate " ,
" type " => " text/html " ,
" href " => $contact [ " alias " ]));
xml :: add_element ( $doc , $source , " link " , " " , array ( " rel " => " self " ,
" type " => " application/atom+xml " ,
" href " => $contact [ " poll " ]));
xml :: add_element ( $doc , $source , " icon " , $contact [ " photo " ]);
xml :: add_element ( $doc , $source , " updated " , datetime_convert ( " UTC " , " UTC " , $contact [ " success_update " ] . " +00:00 " , ATOM_TIME ));
return $source ;
2016-02-05 21:31:11 +01:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Fetches contact data from the contact or the gcontact table
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ param string $url URL of the contact
2016-03-31 07:34:13 +02:00
* @ param array $owner Contact data of the poster
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return array Contact array
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function contact_entry ( $url , $owner ) {
2015-12-12 10:41:42 +01:00
2016-03-30 23:25:20 +02: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-03-21 17:02:59 +01:00
if ( $r ) {
2016-03-30 23:25:20 +02:00
$contact = $r [ 0 ];
$contact [ " uid " ] = - 1 ;
2017-03-21 17:02:59 +01:00
}
if ( ! $r ) {
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1 " ,
dbesc ( normalise_link ( $url )));
2017-03-21 17:02:59 +01:00
if ( $r ) {
2016-03-30 23:25:20 +02:00
$contact = $r [ 0 ];
$contact [ " uid " ] = - 1 ;
$contact [ " success_update " ] = $contact [ " updated " ];
2015-12-12 10:41:42 +01:00
}
}
2015-11-23 18:44:47 +01:00
2017-03-21 17:02:59 +01:00
if ( ! $r )
$contact = owner ;
2016-01-06 22:23:12 +01:00
2016-03-30 23:25:20 +02:00
if ( ! isset ( $contact [ " poll " ])) {
$data = probe_url ( $url );
$contact [ " poll " ] = $data [ " poll " ];
2016-04-02 14:10:40 +02:00
2017-03-21 17:02:59 +01:00
if ( ! $contact [ " alias " ])
2016-04-02 14:10:40 +02:00
$contact [ " alias " ] = $data [ " alias " ];
2015-12-07 21:52:42 +01:00
}
2017-03-21 17:02:59 +01:00
if ( ! isset ( $contact [ " alias " ]))
2016-03-30 23:25:20 +02:00
$contact [ " alias " ] = $contact [ " url " ];
return $contact ;
2015-12-10 18:16:16 +01:00
}
2015-12-07 21:52:42 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds an entry element with reshared content
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02: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
2016-03-31 00:14:51 +02:00
* @ param $repeated_guid
2016-03-31 22:01:56 +02:00
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object Entry element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function reshare_entry ( $doc , $item , $owner , $repeated_guid , $toplevel ) {
if (( $item [ " id " ] != $item [ " parent " ]) AND ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
}
$title = self :: entry_header ( $doc , $entry , $owner , $toplevel );
$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-03-21 17:02:59 +01:00
if ( $r )
$repeated_item = $r [ 0 ];
else
2016-03-30 23:25:20 +02:00
return false ;
$contact = self :: contact_entry ( $repeated_item [ 'author-link' ], $owner );
2015-12-13 14:10:18 +01:00
2015-11-23 18:44:47 +01:00
$parent_item = (( $item [ 'thr-parent' ]) ? $item [ 'thr-parent' ] : $item [ 'parent-uri' ]);
2016-03-30 23:25:20 +02:00
$title = $owner [ " nick " ] . " repeated a notice by " . $contact [ " nick " ];
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
self :: entry_content ( $doc , $entry , $item , $owner , $title , ACTIVITY_SHARE , false );
2015-12-13 14:10:18 +01:00
2016-03-30 23:25:20 +02:00
$as_object = $doc -> createElement ( " activity:object " );
2015-12-13 14:10:18 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $as_object , " activity:object-type " , NAMESPACE_ACTIVITY_SCHEMA . " activity " );
self :: entry_content ( $doc , $as_object , $repeated_item , $owner , " " , " " , false );
$author = self :: add_author ( $doc , $contact );
$as_object -> appendChild ( $author );
$as_object2 = $doc -> createElement ( " activity:object " );
xml :: add_element ( $doc , $as_object2 , " activity:object-type " , self :: construct_objecttype ( $repeated_item ));
$title = sprintf ( " New comment by %s " , $contact [ " nick " ]);
self :: entry_content ( $doc , $as_object2 , $repeated_item , $owner , $title );
$as_object -> appendChild ( $as_object2 );
self :: entry_footer ( $doc , $as_object , $item , $owner , false );
$source = self :: source_entry ( $doc , $contact );
$as_object -> appendChild ( $source );
$entry -> appendChild ( $as_object );
self :: entry_footer ( $doc , $entry , $item , $owner );
return $entry ;
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds an entry element with a " like "
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02: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
2016-03-31 22:01:56 +02:00
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object Entry element with " like "
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function like_entry ( $doc , $item , $owner , $toplevel ) {
if (( $item [ " id " ] != $item [ " parent " ]) AND ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
2015-12-13 14:10:18 +01:00
}
2016-03-30 23:25:20 +02:00
$title = self :: entry_header ( $doc , $entry , $owner , $toplevel );
$verb = NAMESPACE_ACTIVITY_SCHEMA . " favorite " ;
self :: entry_content ( $doc , $entry , $item , $owner , " Favorite " , $verb , false );
$as_object = $doc -> createElement ( " activity:object " );
2016-04-02 17:43:53 +02:00
$parent = q ( " SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d " ,
dbesc ( $item [ " thr-parent " ]), intval ( $item [ " uid " ]));
2016-03-30 23:25:20 +02:00
$parent_item = (( $item [ 'thr-parent' ]) ? $item [ 'thr-parent' ] : $item [ 'parent-uri' ]);
xml :: add_element ( $doc , $as_object , " activity:object-type " , self :: construct_objecttype ( $parent [ 0 ]));
self :: entry_content ( $doc , $as_object , $parent [ 0 ], $owner , " New entry " );
$entry -> appendChild ( $as_object );
self :: entry_footer ( $doc , $entry , $item , $owner );
return $entry ;
2015-12-13 14:10:18 +01:00
}
2015-11-23 18:44:47 +01:00
2016-12-18 18:10:38 +01:00
/**
* @ brief Adds the person object element to the XML document
*
* @ param object $doc XML document
* @ param array $owner Contact data of the poster
* @ param array $contact Contact data of the target
*
* @ return object author element
*/
private function add_person_object ( $doc , $owner , $contact ) {
$object = $doc -> createElement ( " activity:object " );
xml :: add_element ( $doc , $object , " activity:object-type " , ACTIVITY_OBJ_PERSON );
if ( $contact [ 'network' ] == NETWORK_PHANTOM ) {
xml :: add_element ( $doc , $object , " id " , $contact [ 'url' ]);
return $object ;
}
xml :: add_element ( $doc , $object , " id " , $contact [ " alias " ]);
xml :: add_element ( $doc , $object , " title " , $contact [ " nick " ]);
$attributes = array ( " rel " => " alternate " , " type " => " text/html " , " href " => $contact [ " url " ]);
xml :: add_element ( $doc , $object , " link " , " " , $attributes );
$attributes = array (
" rel " => " avatar " ,
" type " => " image/jpeg " , // To-Do?
" media:width " => 175 ,
" media:height " => 175 ,
" href " => $contact [ " photo " ]);
xml :: add_element ( $doc , $object , " link " , " " , $attributes );
xml :: add_element ( $doc , $object , " poco:preferredUsername " , $contact [ " nick " ]);
xml :: add_element ( $doc , $object , " poco:displayName " , $contact [ " name " ]);
if ( trim ( $contact [ " location " ]) != " " ) {
$element = $doc -> createElement ( " poco:address " );
xml :: add_element ( $doc , $element , " poco:formatted " , $contact [ " location " ]);
$object -> appendChild ( $element );
}
return $object ;
}
/**
* @ brief Adds a follow / unfollow entry element
*
* @ 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 ) ?
*
* @ return object Entry element
*/
private function follow_entry ( $doc , $item , $owner , $toplevel ) {
$item [ " id " ] = $item [ " parent " ] = 0 ;
2016-12-18 21:02:13 +01:00
$item [ " created " ] = $item [ " edited " ] = date ( " c " );
$item [ " private " ] = true ;
2016-12-18 18:10:38 +01:00
$contact = Probe :: uri ( $item [ 'follow' ]);
if ( $contact [ 'alias' ] == '' ) {
$contact [ 'alias' ] = $contact [ " url " ];
} else {
$item [ 'follow' ] = $contact [ 'alias' ];
}
2016-12-18 21:02:13 +01:00
$r = q ( " SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' " ,
intval ( $owner [ 'uid' ]), dbesc ( normalise_link ( $contact [ " url " ])));
if ( dbm :: is_result ( $r )) {
$connect_id = $r [ 0 ][ 'id' ];
} else {
$connect_id = 0 ;
}
2016-12-18 18:10:38 +01:00
if ( $item [ 'verb' ] == ACTIVITY_FOLLOW ) {
2016-12-18 21:02:13 +01:00
$message = t ( '%s is now following %s.' );
$title = t ( 'following' );
$action = " subscription " ;
2016-12-18 18:10:38 +01:00
} else {
2016-12-18 21:02:13 +01:00
$message = t ( '%s stopped following %s.' );
$title = t ( 'stopped following' );
$action = " unfollow " ;
2016-12-18 18:10:38 +01:00
}
2016-12-18 21:02:13 +01:00
$item [ " uri " ] = $item [ 'parent-uri' ] = $item [ 'thr-parent' ] =
'tag:' . get_app () -> get_hostname () .
',' . date ( 'Y-m-d' ) . ':' . $action . ':' . $owner [ 'uid' ] .
':person:' . $connect_id . ':' . $item [ 'created' ];
2016-12-18 18:10:38 +01:00
2016-12-18 21:02:13 +01:00
$item [ " body " ] = sprintf ( $message , $owner [ " nick " ], $contact [ " nick " ]);
2016-12-18 18:10:38 +01:00
2016-12-18 21:02:13 +01:00
self :: entry_header ( $doc , $entry , $owner , $toplevel );
self :: entry_content ( $doc , $entry , $item , $owner , $title );
2016-12-18 18:10:38 +01:00
$object = self :: add_person_object ( $doc , $owner , $contact );
$entry -> appendChild ( $object );
self :: entry_footer ( $doc , $entry , $item , $owner );
return $entry ;
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds a regular entry element
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02: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
2016-03-31 22:01:56 +02:00
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return object Entry element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function note_entry ( $doc , $item , $owner , $toplevel ) {
if (( $item [ " id " ] != $item [ " parent " ]) AND ( normalise_link ( $item [ " author-link " ]) != normalise_link ( $owner [ " url " ]))) {
logger ( " OStatus entry is from author " . $owner [ " url " ] . " - not from " . $item [ " author-link " ] . " . Quitting. " , LOGGER_DEBUG );
}
$title = self :: entry_header ( $doc , $entry , $owner , $toplevel );
xml :: add_element ( $doc , $entry , " activity:object-type " , ACTIVITY_OBJ_NOTE );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
self :: entry_content ( $doc , $entry , $item , $owner , $title );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
self :: entry_footer ( $doc , $entry , $item , $owner );
2015-12-13 14:10:18 +01:00
2016-03-30 23:25:20 +02:00
return $entry ;
2015-12-13 14:10:18 +01:00
}
2016-03-30 23:25:20 +02:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds a header element to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:24:54 +02:00
* @ param object $entry The entry element where the elements are added
2016-03-31 07:34:13 +02:00
* @ param array $owner Contact data of the poster
2016-03-31 22:01:56 +02:00
* @ param bool $toplevel Is it for en entry element ( false ) or a feed entry ( true ) ?
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return string The title for the element
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function entry_header ( $doc , & $entry , $owner , $toplevel ) {
2016-03-31 22:01:56 +02:00
/// @todo Check if this title stuff is really needed (I guess not)
2016-03-30 23:25:20 +02:00
if ( ! $toplevel ) {
$entry = $doc -> createElement ( " entry " );
$title = sprintf ( " New note by %s " , $owner [ " nick " ]);
} 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 );
$author = self :: add_author ( $doc , $owner );
$entry -> appendChild ( $author );
$title = sprintf ( " New comment by %s " , $owner [ " nick " ]);
}
return $title ;
2015-12-13 14:10:18 +01:00
}
2015-11-23 18:44:47 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Adds elements to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:01:56 +02:00
* @ param object $entry Entry element where the content is added
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
2016-03-31 22:01:56 +02:00
* @ param string $title Title for the post
* @ param string $verb The activity verb
* @ param bool $complete Add the " status_net " element ?
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
private function entry_content ( $doc , $entry , $item , $owner , $title , $verb = " " , $complete = true ) {
2015-11-23 18:44:47 +01:00
2017-03-21 17:02:59 +01:00
if ( $verb == " " )
2016-03-30 23:25:20 +02:00
$verb = self :: construct_verb ( $item );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " id " , $item [ " uri " ]);
xml :: add_element ( $doc , $entry , " title " , $title );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$body = self :: format_picture_post ( $item [ 'body' ]);
2015-11-23 18:44:47 +01:00
2017-03-21 17:02:59 +01:00
if ( $item [ 'title' ] != " " )
2016-03-30 23:25:20 +02:00
$body = " [b] " . $item [ 'title' ] . " [/b] \n \n " . $body ;
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$body = bbcode ( $body , false , false , 7 );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " content " , $body , array ( " type " => " html " ));
2015-12-07 21:52:42 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " alternate " , " type " => " text/html " ,
" href " => App :: get_baseurl () . " /display/ " . $item [ " guid " ]));
2015-12-07 21:52:42 +01:00
2016-12-18 21:02:13 +01:00
if ( $complete AND ( $item [ " id " ] > 0 ))
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " status_net " , " " , array ( " notice_id " => $item [ " id " ]));
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " activity:verb " , $verb );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " published " , datetime_convert ( " UTC " , " UTC " , $item [ " created " ] . " +00:00 " , ATOM_TIME ));
xml :: add_element ( $doc , $entry , " updated " , datetime_convert ( " UTC " , " UTC " , $item [ " edited " ] . " +00:00 " , ATOM_TIME ));
2015-11-23 18:44:47 +01:00
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:24:54 +02:00
* @ brief Adds the elements at the foot of an entry to the XML document
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param object $doc XML document
2016-03-31 22:24:54 +02:00
* @ param object $entry The entry element where the elements are added
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
2016-03-31 00:14:51 +02:00
* @ param $complete
*/
2016-03-30 23:25:20 +02:00
private function entry_footer ( $doc , $entry , $item , $owner , $complete = true ) {
$mentioned = array ();
2017-04-11 03:11:28 +02:00
if (( $item [ 'parent' ] != $item [ 'id' ]) OR ( $item [ 'parent-uri' ] !== $item [ 'uri' ]) OR (( $item [ 'thr-parent' ] !== '' ) AND ( $item [ 'thr-parent' ] !== $item [ 'uri' ]))) {
2016-03-30 23:25:20 +02: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' ]);
$attributes = array (
" ref " => $parent_item ,
" type " => " text/html " ,
" href " => App :: get_baseurl () . " /display/ " . $parent [ 0 ][ " guid " ]);
xml :: add_element ( $doc , $entry , " thr:in-reply-to " , " " , $attributes );
$attributes = array (
" rel " => " related " ,
" href " => App :: get_baseurl () . " /display/ " . $parent [ 0 ][ " guid " ]);
xml :: add_element ( $doc , $entry , " link " , " " , $attributes );
$mentioned [ $parent [ 0 ][ " author-link " ]] = $parent [ 0 ][ " author-link " ];
$mentioned [ $parent [ 0 ][ " owner-link " ]] = $parent [ 0 ][ " owner-link " ];
$thrparent = q ( " SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `uid` = %d AND `uri` = '%s' " ,
intval ( $owner [ " uid " ]),
dbesc ( $parent_item ));
2017-03-21 17:02:59 +01:00
if ( $thrparent ) {
2016-03-30 23:25:20 +02:00
$mentioned [ $thrparent [ 0 ][ " author-link " ]] = $thrparent [ 0 ][ " author-link " ];
$mentioned [ $thrparent [ 0 ][ " owner-link " ]] = $thrparent [ 0 ][ " owner-link " ];
}
}
2016-12-18 18:10:38 +01:00
if ( intval ( $item [ " parent " ]) > 0 ) {
$conversation = App :: get_baseurl () . " /display/ " . $owner [ " nick " ] . " / " . $item [ " parent " ];
2016-12-18 21:02:13 +01:00
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " ostatus:conversation " , " href " => $conversation ));
2017-04-19 23:37:00 +02:00
$attributes = array (
" href " => $conversation ,
" local_id " => $item [ " parent " ],
" ref " => $conversation );
xml :: add_element ( $doc , $entry , " ostatus:conversation " , $conversation , $attributes );
2016-12-18 18:10:38 +01:00
}
2016-03-30 23:25:20 +02:00
$tags = item_getfeedtags ( $item );
2017-03-21 17:02:59 +01:00
if ( count ( $tags ))
foreach ( $tags as $t )
if ( $t [ 0 ] == " @ " )
2016-03-30 23:25:20 +02:00
$mentioned [ $t [ 1 ]] = $t [ 1 ];
// Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS)
$newmentions = array ();
foreach ( $mentioned AS $mention ) {
$newmentions [ str_replace ( " http:// " , " https:// " , $mention )] = str_replace ( " http:// " , " https:// " , $mention );
$newmentions [ str_replace ( " https:// " , " http:// " , $mention )] = str_replace ( " https:// " , " http:// " , $mention );
}
$mentioned = $newmentions ;
foreach ( $mentioned AS $mention ) {
$r = q ( " SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' " ,
intval ( $owner [ " uid " ]),
dbesc ( normalise_link ( $mention )));
2017-03-21 17:02:59 +01:00
if ( $r [ 0 ][ " forum " ] OR $r [ 0 ][ " prv " ])
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " mentioned " ,
" ostatus:object-type " => ACTIVITY_OBJ_GROUP ,
" href " => $mention ));
2017-03-21 17:02:59 +01:00
else
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " mentioned " ,
" ostatus:object-type " => ACTIVITY_OBJ_PERSON ,
" href " => $mention ));
}
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
if ( ! $item [ " private " ]) {
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " ostatus:attention " ,
" href " => " http://activityschema.org/collection/public " ));
xml :: add_element ( $doc , $entry , " link " , " " , array ( " rel " => " mentioned " ,
" ostatus:object-type " => " http://activitystrea.ms/schema/1.0/collection " ,
" href " => " http://activityschema.org/collection/public " ));
}
2017-03-21 17:02:59 +01:00
if ( count ( $tags ))
foreach ( $tags as $t )
if ( $t [ 0 ] != " @ " )
2016-03-30 23:25:20 +02:00
xml :: add_element ( $doc , $entry , " category " , " " , array ( " term " => $t [ 2 ]));
self :: get_attachment ( $doc , $entry , $item );
2016-12-18 21:02:13 +01:00
if ( $complete AND ( $item [ " id " ] > 0 )) {
2016-03-30 23:25:20 +02:00
$app = $item [ " app " ];
if ( $app == " " )
$app = " web " ;
$attributes = array ( " local_id " => $item [ " id " ], " source " => $app );
if ( isset ( $parent [ " id " ]))
$attributes [ " repeat_of " ] = $parent [ " id " ];
if ( $item [ " coord " ] != " " )
xml :: add_element ( $doc , $entry , " georss:point " , $item [ " coord " ]);
xml :: add_element ( $doc , $entry , " statusnet:notice_info " , " " , $attributes );
}
}
2015-11-23 18:44:47 +01:00
2016-03-31 00:14:51 +02:00
/**
2016-03-31 22:01:56 +02:00
* @ brief Creates the XML feed for a given nickname
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ param app $a The application class
* @ param string $owner_nick Nickname of the feed owner
* @ param string $last_update Date of the last update
2016-03-31 00:14:51 +02:00
*
2016-03-31 22:01:56 +02:00
* @ return string XML feed
2016-03-31 00:14:51 +02:00
*/
2017-01-09 13:09:01 +01:00
public static function feed ( App $a , $owner_nick , $last_update ) {
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$r = q ( " SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
FROM `contact` INNER JOIN `user` ON `user` . `uid` = `contact` . `uid`
WHERE `contact` . `self` AND `user` . `nickname` = '%s' LIMIT 1 " ,
dbesc ( $owner_nick ));
if ( ! $r )
return ;
$owner = $r [ 0 ];
2017-03-21 17:02:59 +01:00
if ( ! strlen ( $last_update ))
2016-03-30 23:25:20 +02:00
$last_update = 'now -30 days' ;
$check_date = datetime_convert ( 'UTC' , 'UTC' , $last_update , 'Y-m-d H:i:s' );
2016-10-21 20:25:21 +02:00
$authorid = get_contact ( $owner [ " url " ], 0 );
$items = q ( " SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`)
STRAIGHT_JOIN `thread` ON `thread` . `iid` = `item` . `parent`
WHERE `item` . `uid` = % d AND `item` . `contact-id` = % d AND
`item` . `author-id` = % d AND `item` . `created` > '%s' AND
NOT `item` . `deleted` AND NOT `item` . `private` AND
`thread` . `network` IN ( '%s' , '%s' )
ORDER BY `item` . `created` DESC LIMIT 300 " ,
intval ( $owner [ " uid " ]), intval ( $owner [ " id " ]),
intval ( $authorid ), dbesc ( $check_date ),
dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ));
2016-10-23 23:59:40 +02:00
/* 2016 - 10 - 23 : The old query will be kept until we are sure that the query above is a good and fast replacement
2016-10-21 20:25:21 +02:00
$items = q ( " SELECT `item`.*, `item`.`id` AS `item_id` FROM `item`
STRAIGHT_JOIN `thread` ON `thread` . `iid` = `item` . `parent`
2016-03-30 23:25:20 +02:00
LEFT JOIN `item` AS `thritem` ON `thritem` . `uri` = `item` . `thr-parent` AND `thritem` . `uid` = `item` . `uid`
WHERE `item` . `uid` = % d AND `item` . `received` > '%s' AND NOT `item` . `private` AND NOT `item` . `deleted`
AND `item` . `allow_cid` = '' AND `item` . `allow_gid` = '' AND `item` . `deny_cid` = '' AND `item` . `deny_gid` = ''
AND (( `item` . `wall` AND ( `item` . `parent` = `item` . `id` ))
OR ( `item` . `network` = '%s' AND (( `thread` . `network` IN ( '%s' , '%s' )) OR ( `thritem` . `network` IN ( '%s' , '%s' )))) AND `thread` . `mention` )
AND (( `item` . `owner-link` IN ( '%s' , '%s' ) AND ( `item` . `parent` = `item` . `id` ))
OR ( `item` . `author-link` IN ( '%s' , '%s' )))
2016-07-19 08:43:57 +02:00
ORDER BY `item` . `id` DESC
2016-03-30 23:25:20 +02:00
LIMIT 0 , 300 " ,
intval ( $owner [ " uid " ]), dbesc ( $check_date ), dbesc ( NETWORK_DFRN ),
//dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
//dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ),
dbesc ( NETWORK_OSTATUS ), dbesc ( NETWORK_DFRN ),
dbesc ( $owner [ " nurl " ]), dbesc ( str_replace ( " http:// " , " https:// " , $owner [ " nurl " ])),
dbesc ( $owner [ " nurl " ]), dbesc ( str_replace ( " http:// " , " https:// " , $owner [ " nurl " ]))
);
2016-10-21 20:25:21 +02:00
*/
2016-03-30 23:25:20 +02:00
$doc = new DOMDocument ( '1.0' , 'utf-8' );
$doc -> formatOutput = true ;
$root = self :: add_header ( $doc , $owner );
foreach ( $items AS $item ) {
$entry = self :: entry ( $doc , $item , $owner );
$root -> appendChild ( $entry );
}
return ( trim ( $doc -> saveXML ()));
}
2016-03-31 00:14:51 +02:00
/**
2016-03-31 07:34:13 +02:00
* @ brief Creates the XML for a salmon message
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ param array $item Data of the item that is to be posted
* @ param array $owner Contact data of the poster
2016-03-31 00:14:51 +02:00
*
2016-03-31 07:34:13 +02:00
* @ return string XML for the salmon
2016-03-31 00:14:51 +02:00
*/
2016-03-30 23:25:20 +02:00
public static function salmon ( $item , $owner ) {
$doc = new DOMDocument ( '1.0' , 'utf-8' );
$doc -> formatOutput = true ;
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$entry = self :: entry ( $doc , $item , $owner , true );
2015-11-23 18:44:47 +01:00
2016-03-30 23:25:20 +02:00
$doc -> appendChild ( $entry );
return ( trim ( $doc -> saveXML ()));
}
2015-11-23 18:44:47 +01:00
}
2015-06-20 14:40:30 +02:00
?>