[ 'name' => 'News Feed', 'type' => 'list', 'title' => 'Feeds from Reuters U.S/International edition', 'values' => [ 'Top News' => 'home/topnews', 'Fact Check' => 'chan:abtpk0vm', 'Entertainment' => 'chan:8ym8q8dl', 'Politics' => 'politics', 'Wire' => 'wire', 'Breakingviews' => '/breakingviews', 'World' => [ 'World' => 'world', 'Africa' => '/world/africa', 'Americas' => '/world/americas', 'Asia-Pacific' => '/world/asia-pacific', 'China' => 'china', 'europe' => '/world/europe', 'India' => '/world/india', 'Middle East' => '/world/middle-east', 'UK' => 'chan:61leiu7j', 'USA News' => 'us', 'The Great Reboot' => '/world/the-great-reboot', 'Reuters Next' => '/world/reuters-next' ], 'Business' => [ 'Business' => 'business', 'Aerospace and Defense' => 'aerospace', 'Autos Transportation' => '/business/autos-transportation', 'Energy' => 'energy', 'Finance' => '/business/finance', 'Health' => 'chan:8hw7807a', 'Media Telecom' => '/business/media-telecom', 'Retail Consumer' => '/business/retail-consumer', 'Sustainable Business' => '/business/sustainable-business', 'Change Suite' => '/business/change-suite', 'Future of Health' => '/business/future-of-health', 'Future of Money' => '/business/future-of-money', 'Take Five' => '/business/take-five', 'Reuters Impact' => '/business/reuters-impact', ], 'Legal' => [ 'Legal' => '/legal', 'Government' => '/legal/government', 'Legal Industry' => '/legal/legalindustry', 'Litigation' => '/legal/litigation', 'Transactional' => '/legal/transactional', ], 'Markets' => [ 'Markets' => 'markets', 'Asian Markets' => '/markets/asia', 'Commodities' => '/markets/commodities', 'Currencies' => '/markets/currencies', 'Deals' => '/markets/deals', 'European Markets' => '/markets/europe', 'Funds' => '/markets/fund', 'Global Market Data' => '/markets/global-market-data', 'Rates & Bonds' => '/markets/rates-bonds', 'Stocks' => '/markets/stocks', 'U.S Markets' => '/markets/us', 'Wealth' => '/markets/wealth', 'Macro Matters' => '/markets/macromatters', ], 'Technology' => [ 'Technology' => 'tech', 'Disrupted' => '/technology/disrupted', 'Reuters Momentum' => '/technology/reuters-momentum', ], 'Sports' => [ 'Sports' => 'sports', 'Athletics' => '/lifestyle/sports/athletics', 'Cricket' => '/lifestyle/sports/cricket', 'Cycling' => '/lifestyle/sports/cycling', 'Golf' => '/lifestyle/sports/golf', 'Motor Sports' => '/lifestyle/sports/motor-sports', 'Soccer' => '/lifestyle/sports/soccer', 'Tennis' => '/lifestyle/sports/tennis', ], 'Lifestyle' => [ 'Lifestyle' => 'life', 'Oddly Enough' => '/lifestyle/oddly-enough', 'Science' => 'science', ] ] ] ] ]; const BACKWARD_COMPATIBILITY = [ 'world' => '/world', 'china' => '/world/china', 'chan:61leiu7j' => '/world/uk', 'us' => '/world/us', 'business' => '/business', 'aerospace' => '/business/aerospace-defense', 'energy' => '/business/energy', 'environment' => '/business/environment', 'chan:8hw7807a' => '/business/healthcare-pharmaceuticals', 'markets' => '/markets', 'tech' => '/technology', 'sports' => '/lifestyle/sports', 'life' => '/lifestyle', 'science' => '/lifestyle/science', 'home/topnews' => '/home', ]; const OLD_WIRE_SECTION = [ 'home/topnews', 'chan:abtpk0vm', 'chan:8ym8q8dl', 'politics', 'wire' ]; /** * Takes in data from Reuters Wire API and * creates structured data in the form of a list * of story information. * @param array $data JSON collected from the Reuters Wire API */ private function processData($data) { /** * Gets a list of wire items which are groups of templates */ $reuters_allowed_wireitems = array_filter( $data, function ($wireitem) { return in_array( $wireitem['wireitem_type'], self::ALLOWED_WIREITEM_TYPES ); } ); /* * Gets a list of "Templates", which is data containing a story */ $reuters_wireitem_templates = array_reduce( $reuters_allowed_wireitems, function (array $carry, array $wireitem) { $wireitem_templates = $wireitem['templates']; return array_merge( $carry, array_filter( $wireitem_templates, function ( array $template_data ) { return in_array( $template_data['type'], self::ALLOWED_TEMPLATE_TYPES ); } ) ); }, [] ); return $reuters_wireitem_templates; } private function getSectionEndpoint() { $endpoint = $this->getInput('feed'); if (isset(self::BACKWARD_COMPATIBILITY[$endpoint])) { $endpoint = self::BACKWARD_COMPATIBILITY[$endpoint]; } elseif (in_array($endpoint, self::OLD_WIRE_SECTION)) { $this->useWireAPI = true; } return $endpoint; } /** * @param string $endpoint - A endpoint is provided could be article URI or ID. * @param string $fetch_type - Provide what kind of fetch do you want? Article or Section. * @param boolean $is_article_uid {true|false} - A boolean flag to determined if using UID instead of url to fetch. * @return string A completed API URL to fetch data */ private function getAPIURL($endpoint, $fetch_type, $is_article_uid = false) { $base_url = self::URI . '/pf/api/v3/content/fetch/'; $wire_url = 'https://wireapi.reuters.com/v8'; switch ($fetch_type) { case 'article': if ($this->useWireAPI) { return $wire_url . $endpoint; } $base_query = [ 'website' => 'reuters', ]; $query = []; if ($is_article_uid) { $query = [ 'id' => $endpoint ]; } else { $query = [ 'website_url' => $endpoint, ]; } $query = array_merge($base_query, $query); $json_query = json_encode($query); return $base_url . 'article-by-id-or-url-v1?query=' . $json_query; break; case 'section': if ($this->useWireAPI) { if (strpos($endpoint, 'chan:') !== false) { // Now checking whether that feed has unique ID or not. $feed_uri = "/feed/rapp/us/wirefeed/$endpoint"; } else { $feed_uri = "/feed/rapp/us/tabbar/feeds/$endpoint"; } return $wire_url . $feed_uri; } $query = [ 'section_id' => $endpoint, 'size' => 30, 'website' => 'reuters' ]; if ($endpoint != '/home') { $query = array_merge($query, [ 'fetch_type' => 'section', ]); } $json_query = json_encode($query); return $base_url . 'articles-by-section-alias-or-id-v1?query=' . $json_query; break; } returnServerError('unsupported endpoint'); } private function addStories($title, $content, $timestamp, $author, $url, $category) { $item = []; $item['categories'] = $category; $item['author'] = $author; $item['content'] = $content; $item['title'] = $title; $item['timestamp'] = $timestamp; $item['uri'] = $url; $this->items[] = $item; } private function getArticle($feed_uri, $is_article_uid = false) { // This will make another request to API to get full detail of article and author's name. $url = $this->getAPIURL($feed_uri, 'article', $is_article_uid); try { $json = getContents($url); $rawData = Json::decode($json); } catch (\JsonException $e) { return [ 'content' => '', 'author' => '', 'category' => '', 'images' => '', 'published_at' => '' ]; } $article_content = ''; $authorlist = ''; $category = []; $image_list = []; $published_at = ''; if ($this->useWireAPI) { $reuters_wireitems = $rawData['wireitems']; $processedData = $this->processData($reuters_wireitems); $first = reset($processedData); $article_content = $first['story']['body_items']; $authorlist = $first['story']['authors']; $category = [$first['story']['channel']['name']]; $image_list = $first['story']['images']; $published_at = $first['story']['published_at']; } else { $article_content = $rawData['result']['content_elements']; $authorlist = $rawData['result']['authors']; $category = [$rawData['result']['taxonomy']['ads_primary_section']['name']]; $image_list = []; if (!empty($rawData['result']['related_content']['galleries'])) { $galleries = $rawData['result']['related_content']['galleries']; foreach ($galleries as $gallery) { $image_list = array_merge($image_list, $gallery['content_elements']); } } elseif (!empty($rawData['result']['related_content']['images'])) { $image_list = $rawData['result']['related_content']['images']; } $published_at = $rawData['result']['published_time']; } $content_detail = [ 'content' => $this->handleArticleContent($article_content), 'author' => $this->handleAuthorName($authorlist), 'category' => $category, 'images' => $this->handleImage($image_list), 'published_at' => $published_at ]; return $content_detail; } private function handleImage($images) { $img_placeholder = ''; foreach ($images as $image) { // Add more image to article. $image_url = $image['url']; $image_caption = $image['caption'] ?? $image['alt_text'] ?? $image['subtitle'] ?? ''; $image_alt_text = ''; $image_alt_text = $image['alt_text'] ?? $image_caption; $img = "\"$image_alt_text\""; $img_caption = "
$image_caption
"; $figure = "
$img \t $img_caption
"; $img_placeholder = $img_placeholder . $figure; } return $img_placeholder; } private function handleAuthorName($authors) { $author = ''; $counter = 0; foreach ($authors as $data) { //Formatting author's name. $name = $data['name']; $counter++; if ($counter == count($authors)) { $author .= $name; } else { $author .= $name . ', '; } } return $author; } private function handleArticleContent($contents) { $description = ''; foreach ($contents as $content) { $data = ''; if (isset($content['content'])) { $data = $content['content']; } switch ($content['type']) { case 'paragraph': $description = $description . "

$data

"; break; case 'heading': $description = $description . "

$data

"; break; case 'infographics': $description = $description . ""; break; case 'inline_items': $item_list = $content['items']; $description = $description . '

'; foreach ($item_list as $item) { if ($item['type'] == 'text') { $description = $description . $item['content']; } else { $description = $description . $item['symbol']; } } $description = $description . '

'; break; case 'p_table': $description = $description . $content['content']; break; case 'upstream_embed': $media_type = $content['media_type']; $cid = $content['cid']; $embed = ''; switch ($media_type) { case 'tweet': try { $tweet_url = "https://twitter.com/dummyname/statuses/$cid"; $get_embed_url = 'https://publish.twitter.com/oembed?url=' . urlencode($tweet_url) . '&partner=&hide_thread=false'; $oembed_json = json_decode(getContents($get_embed_url), true); $embed .= $oembed_json['html']; } catch (Exception $e) { // In case not found any tweet. $embed .= ''; } break; case 'instagram': $url = "https://instagram.com/p/$cid/media/?size=l"; $embed .= << EOD; break; case 'youtube': $url = "https://www.youtube.com/embed/$cid"; $embed .= << EOD; break; } $description .= $embed; break; case 'social_media': if ($content['sub_type'] == 'twitter') { $description .= $content['html']; } break; case 'table': $table = ''; $theaders = $content['header'] ?? null; if ($theaders) { $tr = ''; foreach ($theaders as $header) { $tr .= ''; } $tr .= ''; $table .= $tr; } $rows = $content['rows']; foreach ($rows as $row) { if (!is_array($row)) { // some rows are null continue; } $tr = ''; foreach ($row as $data) { $tr .= ''; } $tr .= ''; $table .= $tr; } $table .= '
' . $header . '
' . $data . '
'; $description .= $table; break; case 'image': $description .= $this->handleImage([$content]); } } return $description; } public function getName() { return $this->feedName; } public function collectData() { $endpoint = $this->getSectionEndpoint(); $url = $this->getAPIURL($endpoint, 'section'); $json = getContents($url); $data = Json::decode($json); $stories = []; $section_name = ''; if ($this->useWireAPI) { $reuters_wireitems = $data['wireitems']; $section_name = $data['wire_name']; $processedData = $this->processData($reuters_wireitems); // Merge all articles from Editor's Highlight section into existing array of templates. $top_section = reset($processedData); if ($top_section['type'] == 'headlines') { $top_section = array_shift($processedData); $articles = $top_section['headlines']; $processedData = array_merge($articles, $processedData); } $stories = $processedData; } else { $section_name = $data['result']['section']['name']; if (isset($data['arcResult']['articles'])) { $stories = $data['arcResult']['articles']; } else { $stories = $data['result']['articles']; } } $this->feedName = $section_name . ' | Reuters'; foreach ($stories as $story) { $uid = ''; $author = ''; $category = []; $content = ''; $title = ''; $timestamp = ''; $url = ''; $article_uri = ''; $source_type = ''; if ($this->useWireAPI) { $uid = $story['story']['usn']; $article_uri = $story['template_action']['api_path']; $title = $story['story']['hed']; $url = $story['template_action']['url']; } else { $uid = $story['id']; $url = self::URI . $story['canonical_url']; $title = $story['title']; $article_uri = $story['canonical_url']; $source_type = $story['source']['name']; } // Some article cause unexpected behaviour like redirect to another site not API. // Attempt to check article source type to avoid this. if (!$this->useWireAPI && $source_type != 'Package') { // Only Reuters PF api have this, Wire don't. $author = $this->handleAuthorName($story['authors'] ?? []); $timestamp = $story['published_time']; $image_placeholder = ''; if (isset($story['thumbnail'])) { $image_placeholder = $this->handleImage([$story['thumbnail']]); } $content = $story['description'] . $image_placeholder; if (isset($story['primary_section']['name'])) { $category = [$story['primary_section']['name']]; } else { $category = []; } } else { $content_detail = $this->getArticle($article_uri); $description = $content_detail['content']; $description = defaultLinkTo($description, $this->getURI()); $author = $content_detail['author']; $images = $content_detail['images']; $category = $content_detail['category']; $content = "$description $images"; $timestamp = $content_detail['published_at']; } $this->addStories($title, $content, $timestamp, $author, $url, $category); } } }