diff --git a/include/api.php b/include/api.php index c86a3cbe4b..832240f716 100644 --- a/include/api.php +++ b/include/api.php @@ -250,7 +250,7 @@ */ function api_call(&$a){ GLOBAL $API, $called_api; - + $type="json"; if (strpos($a->query_string, ".xml")>0) $type="xml"; if (strpos($a->query_string, ".json")>0) $type="json"; @@ -653,6 +653,13 @@ } + /** + * @brief return api-formatted array for item's author and owner + * + * @param App $a + * @param array $item : item from db + * @return array(array:author, array:owner) + */ function api_item_get_user(&$a, $item) { // Make sure that there is an entry in the global contacts for author and owner @@ -662,20 +669,16 @@ get_gcontact_id(array("url" => $item['owner-link'], "network" => $item['network'], "photo" => $item['owner-avatar'], "name" => $item['owner-name'])); - // Comments in threads may appear as wall-to-wall postings. - // So only take the owner at the top posting. - if ($item["id"] == $item["parent"]) - $status_user = api_get_user($a,$item["owner-link"]); - else - $status_user = api_get_user($a,$item["author-link"]); - + $status_user = api_get_user($a,$item["author-link"]); $status_user["protected"] = (($item["allow_cid"] != "") OR ($item["allow_gid"] != "") OR ($item["deny_cid"] != "") OR ($item["deny_gid"] != "") OR $item["private"]); - return ($status_user); + $owner_user = api_get_user($a,$item["owner-link"]); + + return (array($status_user, $owner_user)); } @@ -688,8 +691,9 @@ function api_array_to_xml($data, $ename="") { $attrs=""; $childs=""; - if (count($data)==1 && !is_array($data[0])) { + if (count($data)==1 && !is_array($data[array_keys($data)[0]])) { $ename = array_keys($data)[0]; + $ename = trim($ename,'$'); $v = $data[$ename]; return "<$ename>$v"; } @@ -720,7 +724,7 @@ case "xml": $data = array_xmlify($data); if ($templatename==="") { - $ret = api_array_to_xml($data); + $ret = api_array_to_xml($data); } else { $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); if(! $tpl) { @@ -2044,7 +2048,6 @@ } function api_convert_item($item) { - $body = $item['body']; $attachments = api_get_attachments($body); @@ -2082,7 +2085,12 @@ $entities = api_get_entitities($statustext, $body); - return(array("text" => $statustext, "html" => $statushtml, "attachments" => $attachments, "entities" => $entities)); + return array( + "text" => $statustext, + "html" => $statushtml, + "attachments" => $attachments, + "entities" => $entities + ); } function api_get_attachments(&$body) { @@ -2277,6 +2285,33 @@ return $text; } + + /** + * @brief return name as array + * + * @param string $txt + * @return array + * name => 'name' + * 'url => 'url' + */ + function api_contactlink_to_array($txt) { + $match = array(); + $r = preg_match_all('|([^<]*)|', $txt, $match); + if ($r && count($match)==3) { + $res = array( + 'name' => $match[2], + 'url' => $match[1] + ); + } else { + $res = array( + 'name' => $text, + 'url' => "" + ); + } + return $res; + } + + /** * @brief return likes, dislikes and attend status for item * @@ -2285,7 +2320,7 @@ * likes => int count * dislikes => int count */ - function api_format_items_likes(&$item) { + function api_format_items_activities(&$item) { $activities = array( 'like' => array(), 'dislike' => array(), @@ -2302,9 +2337,9 @@ } $res = array(); - $uri = $item['uri']; + $uri = $item['uri']."-l"; foreach($activities as $k => $v) { - $res[$k] = (x($v,$uri)?$v[$uri]:0); + $res[$k] = ( x($v,$uri) ? array_map("api_contactlink_to_array", $v[$uri]) : array() ); } return $res; @@ -2323,10 +2358,9 @@ $ret = Array(); foreach($r as $item) { - api_share_as_retweet($item); localize_item($item); - $status_user = api_item_get_user($a,$item); + list($status_user, $owner_user) = api_item_get_user($a,$item); // Look if the posts are matching if they should be filtered by user id if ($filter_user AND ($status_user["id"] != $user_info["id"])) @@ -2387,10 +2421,11 @@ 'geo' => NULL, 'favorited' => $item['starred'] ? true : false, 'user' => $status_user , + 'friendica_owner' => $owner_user, //'entities' => NULL, 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $item['parent'], - 'friendica_activities' => api_format_items_likes($item), + 'friendica_activities' => api_format_items_activities($item), ); if (count($converted["attachments"]) > 0) @@ -2407,15 +2442,31 @@ // Retweets are only valid for top postings // It doesn't work reliable with the link if its a feed - $IsRetweet = ($item['owner-link'] != $item['author-link']); - if ($IsRetweet) - $IsRetweet = (($item['owner-name'] != $item['author-name']) OR ($item['owner-avatar'] != $item['author-avatar'])); + #$IsRetweet = ($item['owner-link'] != $item['author-link']); + #if ($IsRetweet) + # $IsRetweet = (($item['owner-name'] != $item['author-name']) OR ($item['owner-avatar'] != $item['author-avatar'])); - if ($IsRetweet AND ($item["id"] == $item["parent"])) { - $retweeted_status = $status; - $retweeted_status["user"] = api_get_user($a,$item["author-link"]); - $status["retweeted_status"] = $retweeted_status; + if ($item["id"] == $item["parent"]) { + $retweeted_item = api_share_as_retweet($item); + if ($retweeted_item !== false) { + $retweeted_status = $status; + try { + $retweeted_status["user"] = api_get_user($a,$retweeted_item["author-link"]); + } catch( BadRequestException $e ) { + // user not found. should be found? + /// @todo check if the user should be always found + $retweeted_status["user"] = array(); + } + + $rt_converted = api_convert_item($retweeted_item); + + $retweeted_status['text'] = $rt_converted["text"]; + $retweeted_status['statusnet_html'] = $rt_converted["html"]; + $retweeted_status['friendica_activities'] = api_format_items_activities($retweeted_item); + $retweeted_status['created_at'] = api_date($retweeted_item['created']); + $status['retweeted_status'] = $retweeted_status; + } } // "uid" and "self" are only needed for some internal stuff, so remove it from here @@ -2974,23 +3025,29 @@ } api_register_func('api/friendica/remoteauth', 'api_friendica_remoteauth', true); - + /** + * @brief Return the item shared, if the item contains only the [share] tag + * + * @param array $item Sharer item + * @return array Shared item or false if not a reshare + */ function api_share_as_retweet(&$item) { $body = trim($item["body"]); - // Skip if it isn't a pure repeated messages - // Does it start with a share? - if (strpos($body, "[share") > 0) - return(false); - - // Does it end with a share? - if (strlen($body) > (strrpos($body, "[/share]") + 8)) - return(false); + if (diaspora::is_reshare($body, false)===false) { + return false; + } $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); - // Skip if there is no shared message in there - if ($body == $attributes) - return(false); + // Skip if there is no shared message in there + // we already checked this in diaspora::is_reshare() + // but better one more than one less... + if ($body == $attributes) + return false; + + + // build the fake reshared item + $reshared_item = $item; $author = ""; preg_match("/author='(.*?)'/ism", $attributes, $matches); @@ -3028,18 +3085,31 @@ if ($matches[1] != "") $link = $matches[1]; + $posted = ""; + preg_match("/posted='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $posted= $matches[1]; + + preg_match('/posted="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $posted = $matches[1]; + $shared_body = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$2",$body); - if (($shared_body == "") OR ($profile == "") OR ($author == "") OR ($avatar == "")) - return(false); + if (($shared_body == "") || ($profile == "") || ($author == "") || ($avatar == "") || ($posted == "")) + return false; - $item["body"] = $shared_body; - $item["author-name"] = $author; - $item["author-link"] = $profile; - $item["author-avatar"] = $avatar; - $item["plink"] = $link; - return(true); + + $reshared_item["body"] = $shared_body; + $reshared_item["author-name"] = $author; + $reshared_item["author-link"] = $profile; + $reshared_item["author-avatar"] = $avatar; + $reshared_item["plink"] = $link; + $reshared_item["created"] = $posted; + $reshared_item["edited"] = $posted; + + return $reshared_item; } @@ -3442,16 +3512,16 @@ if (api_user()===false) throw new ForbiddenException(); if ($a->argc!==3) throw new BadRequestException("Invalid argument count"); $nm = new NotificationsManager(); - + $notes = $nm->getAll(array(), "+seen -date", 50); return api_apply_template("", $type, array('$notes' => $notes)); } - + /** * @brief Set notification as seen and returns associated item (if possible) * * POST request with 'id' param as notification id - * + * * @param App $a * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' * @return string @@ -3459,13 +3529,13 @@ function api_friendica_notification_seen(&$a, $type){ if (api_user()===false) throw new ForbiddenException(); if ($a->argc!==4) throw new BadRequestException("Invalid argument count"); - + $id = (x($_REQUEST, 'id') ? intval($_REQUEST['id']) : 0); - - $nm = new NotificationsManager(); + + $nm = new NotificationsManager(); $note = $nm->getByID($id); if (is_null($note)) throw new BadRequestException("Invalid argument"); - + $nm->setSeen($note); if ($note['otype']=='item') { // would be really better with an ItemsManager and $im->getByID() :-P @@ -3481,13 +3551,13 @@ return api_apply_template("timeline", $type, $data); } // the item can't be found, but we set the note as seen, so we count this as a success - } + } return api_apply_template('', $type, array('status' => "success")); } - + api_register_func('api/friendica/notification/seen', 'api_friendica_notification_seen', true, API_METHOD_POST); api_register_func('api/friendica/notification', 'api_friendica_notification', true, API_METHOD_GET); - + /* To.Do: diff --git a/view/templates/api_friends_xml.tpl b/view/templates/api_friends_xml.tpl index 6520415a77..4c9f7e6287 100644 --- a/view/templates/api_friends_xml.tpl +++ b/view/templates/api_friends_xml.tpl @@ -1,8 +1,6 @@ - - - +{{* used in include/api.php 'api_statuses_friends' and 'api_statuses_followers' *}} {{foreach $users as $u}} - {{include file="api_user_xml.tpl" user=$u}} + {{include file="api_user_xml.tpl" user=$u}} {{/foreach}} diff --git a/view/templates/api_single_status_xml.tpl b/view/templates/api_single_status_xml.tpl new file mode 100644 index 0000000000..88c56f935b --- /dev/null +++ b/view/templates/api_single_status_xml.tpl @@ -0,0 +1,25 @@ +{{* shared structure for statuses. includers must define root element *}} + {{$status.text}} + {{$status.truncated}} + {{$status.created_at}} + {{$status.in_reply_to_status_id}} + {{$status.source}} + {{$status.id}} + {{$status.in_reply_to_user_id}} + {{$status.in_reply_to_screen_name}} + {{$status.geo}} + {{$status.favorited}} + {{include file="api_user_xml.tpl" user=$status.user}} + {{include file="api_user_xml.tpl" user=$status.friendica_owner}} + {{$status.statusnet_html}} + {{$status.statusnet_conversation_id}} + {{$status.url}} + {{$status.coordinates}} + {{$status.place}} + {{$status.contributors}} + {{if $status.retweeted_status}}{{include file="api_single_status_xml.tpl" status=$status.retweeted_status}}{{/if}} + + {{foreach $status.friendica_activities as $k=>$v}} + {{$v|count}} + {{/foreach}} + \ No newline at end of file diff --git a/view/templates/api_status_xml.tpl b/view/templates/api_status_xml.tpl index c3b702baab..a382810ff4 100644 --- a/view/templates/api_status_xml.tpl +++ b/view/templates/api_status_xml.tpl @@ -1,47 +1,8 @@ - -{{if $status}} - {{$status.created_at}} - {{$status.id}} - {{$status.text}} - {{$status.source}} - {{$status.truncated}} - {{$status.in_reply_to_status_id}} - {{$status.in_reply_to_user_id}} - {{$status.favorited}} - {{$status.in_reply_to_screen_name}} - {{$status.geo}} - {{$status.coordinates}} - {{$status.place}} - {{$status.contributors}} - - {{$status.user.id}} - {{$status.user.name}} - {{$status.user.screen_name}} - {{$status.user.location}} - {{$status.user.description}} - {{$status.user.profile_image_url}} - {{$status.user.url}} - {{$status.user.protected}} - {{$status.user.followers}} - {{$status.user.profile_background_color}} - {{$status.user.profile_text_color}} - {{$status.user.profile_link_color}} - {{$status.user.profile_sidebar_fill_color}} - {{$status.user.profile_sidebar_border_color}} - {{$status.user.friends_count}} - {{$status.user.created_at}} - {{$status.user.favourites_count}} - {{$status.user.utc_offset}} - {{$status.user.time_zone}} - {{$status.user.profile_background_image_url}} - {{$status.user.profile_background_tile}} - {{$status.user.profile_use_background_image}} - - {{$status.user.geo_enabled}} - {{$status.user.verified}} - - {{$status.user.statuses_count}} - {{$status.user.lang}} - {{$status.user.contributors_enabled}} - -{{/if}} +{{* used in api.php to return a single status *}} + + {{if $status}} + {{include file="api_single_status_xml.tpl" status=$status}} + {{/if}} + diff --git a/view/templates/api_timeline_xml.tpl b/view/templates/api_timeline_xml.tpl index e22c1e2ee5..01b71c0bcc 100644 --- a/view/templates/api_timeline_xml.tpl +++ b/view/templates/api_timeline_xml.tpl @@ -2,25 +2,9 @@ -{{foreach $statuses as $status}} - {{$status.text}} - {{$status.truncated}} - {{$status.created_at}} - {{$status.in_reply_to_status_id}} - {{$status.source}} - {{$status.id}} - {{$status.in_reply_to_user_id}} - {{$status.in_reply_to_screen_name}} - {{$status.geo}} - {{$status.favorited}} -{{include file="api_user_xml.tpl" user=$status.user}} {{$status.statusnet_html}} - {{$status.statusnet_conversation_id}} - {{$status.url}} - {{$status.coordinates}} - {{$status.place}} - {{$status.contributors}} - {{foreach $status.friendica_activities as $k=>$v}} - {{$v}} - {{/foreach}} +{{foreach $statuses as $status}} + + {{include file="api_single_status_xml.tpl" status=$status}} -{{/foreach}} +{{/foreach}} + diff --git a/view/templates/api_user_xml.tpl b/view/templates/api_user_xml.tpl index 7e0ec6ed63..698c5436dd 100644 --- a/view/templates/api_user_xml.tpl +++ b/view/templates/api_user_xml.tpl @@ -1,5 +1,5 @@ +{{* includer template MUST provide root element *}} - {{$user.id}} {{$user.name}} {{$user.screen_name}} @@ -44,4 +44,4 @@ {{$user.status.place}} {{$user.status.contributors}} {{/if}} - +