KOK - MANAGER
Edit File: unitecreator_rss.class.php
<?php /** * @package Unlimited Elements * @author unlimited-elements.com * @copyright (C) 2021 Unlimited Elements, All Rights Reserved. * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html */ if ( ! defined( 'ABSPATH' ) ) exit; class UniteCreatorRSS{ public static $showDebug = false; private $rssAutoDetectKeys = array( 'title_key' => 'title', 'description_key' => 'description', 'content_key' => 'content', 'author_key' =>'dc_creator|author_name|author|dc-creator|credit', 'link_key' => 'link|atom_link_href|link_href', 'image_key' => 'media_thumbnail_url|media_group_media_content_url|media_content_media_thumbnail_url|media_content_url|image_url|enclosure_url', 'date_key' => 'publish_date|published|publish_date_original' ); /** * constructor */ public function __construct(){ if(self::$showDebug == false){ $rssDebug = HelperUC::hasPermissionsFromQuery("ucrssdebug"); if($rssDebug == true) self::$showDebug = true; } } /** * get rss fields */ public function getRssFields($name = null) { $fields = array( array( "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, "id" => "rss_url", "text" => __("RSS URL", "unlimited-elements-for-elementor"), "desc" => __("Enter some RSS service url. Example: https://wired.com/feed/rss", "unlimited-elements-for-elementor"), "placeholder" => "Example: https://wired.com/feed/rss", "label_block" => true, "default" => "https://wired.com/feed/rss" ), array( "type" => UniteCreatorDialogParam::PARAM_RADIOBOOLEAN, "id" => "rss_show_filter_by_date", "text" => "Filter By Date", "default" => "false" ), array( "type" => UniteCreatorDialogParam::PARAM_DROPDOWN, "id" => "rss_filter_by_date_option", "text" => "Select value", "conditions" => array("rss_show_filter_by_date"=>"true"), "options" => array( "all" => __("All", "unlimited-elements-for-elementor"), "today" => __("Today", "unlimited-elements-for-elementor"), "yesterday" => __("Yesterday", "unlimited-elements-for-elementor"), "last_2_days" => __("Last 2 Days", "unlimited-elements-for-elementor"), "last_3_days" => __("Last 3 Days", "unlimited-elements-for-elementor") ), "default" => "all" ), array( "type" => UniteCreatorDialogParam::PARAM_RADIOBOOLEAN, "id" => "rss_show_date_formatted", "text" => "Format Date", "default" => "true" ), array( "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, "id" => "rss_date_format", "conditions" => array("rss_show_date_formatted"=>"true"), "text" => "", "desc" => __("Specify the <a target='_blank' href='https://www.php.net/manual/en/datetime.format.php'>date format</a>, default is 'd/m/Y, H:i'", "unlimited-elements-for-elementor"), "placeholder" => "d/m/Y, H:i", "default" => "d/m/Y, H:i" ), array( "type" => UniteCreatorDialogParam::PARAM_TEXTFIELD, "id" => "rss_items_limit", "text" => __("Items Limit", "unlimited-elements-for-elementor"), "desc" => __("Optional. You can specify the maximum number of items: from 1 to 50., Use 0 for all", "unlimited-elements-for-elementor"), "placeholder" => "0", "default" => "0" ), ); return $fields; } /** * get Rss Feed data */ public function getRssFeedData($data, $arrValues, $name, $showDebug = false){ if(self::$showDebug == true) $showDebug = true; $data[$name] = array(); if($showDebug == true){ dmp("---- the debug is ON, please turn it off before release --- "); } if(empty($arrValues)){ if($showDebug) dmp("no data found"); return($data); } $rss_url = UniteFunctionsUC::getVal($arrValues, $name.'_rss_url'); if(empty($rss_url)){ if($showDebug) dmp("no url found for rss"); return($data); } $getUrlDebug = false; if(self::$showDebug == true) $getUrlDebug = true; if($showDebug){ dmp("Get rss data from: $rss_url"); } try{ $rssContent = HelperUC::$operations->getUrlContents($rss_url, $getUrlDebug, true); }catch(Exception $e){ if($showDebug){ dmp("Failed to fetch rss: ".$e->getMessage()); } return($data); } //dmp($response);exit(); if(empty($rssContent)) { if($showDebug) dmp("no content found for rss"); return($data); } $arrData = UniteFunctionsUC::maybeXmlDecode($rssContent); if(empty($arrData)) { if($showDebug) dmp('no data found for rss'); return($data); } if($showDebug == true && is_array($arrData)) { dmp("Original rss data"); HelperHtmlUC::putHtmlDataDebugBox($arrData); } if(is_array($arrData) == false){ if($showDebug == true){ dmp("No RSS data found. The input is: "); echo "<div style='background-color:lightgray'>"; dmp(htmlspecialchars($rssContent)); echo "</div>"; } return($data); } if(!empty($arrData)) { (bool) $showDateFormated = UniteFunctionsUC::getVal($arrValues, $name."_rss_show_date_formatted"); $dateFormat = null; if ($showDateFormated) { $dateFormat = UniteFunctionsUC::getVal($arrValues, $name."_rss_date_format"); if (empty($dateFormat)) { $dateFormat = 'd/m/Y, H:i'; } } $arrData = $this->simplifyRssDataArray($arrData, $dateFormat); // trim by date limits (bool) $showFilterByDate = UniteFunctionsUC::getVal($arrValues, $name."_rss_show_filter_by_date"); if($showFilterByDate && !empty($arrData)) { $filterByDateOption = UniteFunctionsUC::getVal($arrValues, $name . "_rss_filter_by_date_option"); if (!empty($filterByDateOption) && $filterByDateOption != 'all') { $arrData = $this->limitArrayByPublishDate($arrData, $filterByDateOption); } } // trim by items limit (int) $dataItemsLimit = UniteFunctionsUC::getVal($arrValues, $name."_rss_items_limit"); if($dataItemsLimit > 0 && !empty($arrData)) $arrData = array_slice($arrData, 0, $dataItemsLimit, true); $data[$name] = $arrData; } return $data; } /** * get the date */ private function getDateString($arrItem){ if (array_key_exists('publish_date_original', $arrItem)) return($arrItem["publish_date_original"]); if (array_key_exists('publish_date', $arrItem)) return($arrItem["publish_date"]); return(null); } /** * limit the array (filter) by publish date */ private function limitArrayByPublishDate($arrData, $option){ // calculate period $from_time = 0; $to_time = 0; switch($option){ case 'today': $from_time = strtotime('today midnight'); $to_time = strtotime('tomorrow midnight'); break; case 'yesterday': $from_time = strtotime('yesterday midnight'); $to_time = strtotime('today midnight'); break; case 'last_2_days': $from_time = strtotime("-2 days");; $to_time = time(); break; case 'last_3_days': $from_time = strtotime("-3 days"); $to_time = time(); break; default: break; } if ($from_time > 0 && $to_time > 0) { $newArray = array(); foreach ($arrData as $key => $value){ if (array_key_exists('publish_date', $value)) { $dateString = $this->getDateString($value); $timestamp = UniteFunctionsUC::date2Timestamp($dateString); if ($timestamp >= $from_time && $timestamp < $to_time) { $newArray[] = $arrData[$key]; } } else { $newArray[] = $arrData[$key]; } } return $newArray; } return $arrData; } /** * modify rss array to simplify the use */ private function simplifyRssDataArray($arrRss, $dateFormat = null){ if($items = $this->findRssItems($arrRss)) { $items = $this->createNiceKeys($items, $dateFormat); if (!empty($items)) $arrRss = $items; } else $arrRss = $this->createNiceKeys($arrRss, $dateFormat); if(empty($arrRss)) return($arrRss); //detect image url $firstRssElement = UniteFunctionsUC::getArrFirstValue($arrRss); $hasImageKey = $this->hasImageKey($firstRssElement); if($hasImageKey == true) return($arrRss); $isAddedImage = true; foreach ($arrRss as $rssKey => $rssItem) { $imageLink = $this->getFirstImageLinkFromContent($rssItem); if(!empty($imageLink)){ $isAddedImage = true; $arrRss[$rssKey]['image_url'] = $imageLink; } } //add image key to others if($isAddedImage == true){ foreach ($arrRss as $rssKey => $rssItem) { if(array_key_exists("image_url", $arrRss[$rssKey]) == false) $arrRss[$rssKey]["image_url"] = ""; } } return($arrRss); } /** * create nice keys */ private function createNiceKeys($arrRss, $dateFormat, $prefix = '') { $niceArr = array(); foreach ($arrRss as $key => $value) { $key = strtolower($key); // Replace colons with underscores and append to prefix $newKey = $prefix . ($prefix ? '_' : '') . str_replace(':', '_', $key); if (is_array($value)) { // If the key is numeric, keep it as part of the array if (is_numeric($key)) { $niceArr[$key] = $this->createNiceKeys($value, $dateFormat); } else { $niceArr = array_merge($niceArr, $this->createNiceKeys($value, $dateFormat, $newKey)); } } else { if (is_numeric($key)) { $niceArr[$prefix][$key] = $value; } else { $niceArr[$newKey] = $value; if (!empty($dateFormat) && ($newKey == 'publish_date' || $newKey == 'published')) { $date_time = UniteFunctionsUC::date2Timestamp($value); if (!empty($date_time)){ $niceArr['publish_date_original'] = $value; $niceArr[$key] = uelm_date($dateFormat, $date_time); } } } } } return $niceArr; } /** * create data for rss */ private function createDate($arrRss, $dateFormat) { $niceArr = array(); foreach ($arrRss as $key => $value) { if (is_array($value)) { $niceArr[$key] = $this->createDate($value, $dateFormat); } else { $timestamp = UniteFunctionsUC::date2Timestamp($value); if (!empty($timestamp)) { $formatedDate = uelm_date($dateFormat, $timestamp); if (empty($formatedDate)) { $formatedDate = uelm_date('d/m/Y H:i:s', $timestamp); } $niceArr[$key] = $formatedDate; } else { $niceArr[$key] = $value; } } } return $niceArr; } /** * find rss items in rss array */ private function findRssItems($arrRss) { if (array_key_exists('item', $arrRss)) { return $arrRss['item']; } elseif (array_key_exists('entry', $arrRss)) { return $arrRss['entry']; } else { foreach ($arrRss as $value) { if (is_array($value)) { if ($items = $this->findRssItems($value)) { return $items; } } } } return false; } /** * Filter widget params to get only a specific sublist by catid */ public function filterByTypeAndCatId($array, $type, $catIdValue) { $result = array(); foreach ($array as $key => $subArray) { if ($subArray['type'] != $type) { continue; } if ($subArray[GlobalsUC::ATTR_CATID] != $catIdValue) { continue; } $result[] = $subArray; } return $result; } /** * Get all rss widget keys, if any key is empty use default key for it */ public function getRssFeedKeys(UniteCreatorAddonWork $addon, $data){ $params = $addon->getParams(); $keys = array(); $rssFeedArr = UniteFunctionsUC::getVal($data, "rss_feed"); if (empty($rssFeedArr)) { return; } $firstRssElement = UniteFunctionsUC::getArrFirstValue($rssFeedArr); if (empty($firstRssElement)) { return; } $catValue = $addon->getParamByName("auto_detect_keys"); if (empty($catValue)) { return; } $rssKeyArr = $this->filterByTypeAndCatId($params, UniteCreatorDialogParam::PARAM_TEXTFIELD, $catValue[GlobalsUC::ATTR_CATID]); $isAutoDetect = UniteFunctionsUC::getVal($data, "auto_detect_keys"); $isAutoDetect = UniteFunctionsUC::strToBool($isAutoDetect); $isShowDebug = UniteFunctionsUC::getVal($data, "show_debug"); $isShowDebug = UniteFunctionsUC::strToBool($isShowDebug); if ($isShowDebug) { dmp('--- Fields Keys ---'); } foreach ($rssKeyArr as $keyValue) { $keyName = $keyValue['name']; $defaultKey = null; if ($isAutoDetect == false) { $defaultKey = UniteFunctionsUC::getVal($keyValue, 'value'); } $isAutoKey = false; if (!empty($defaultKey)) { $keys[$keyName] = $defaultKey; } else { $autoKey = $this->autoDetectKeys($keyName, $firstRssElement); if (!empty($autoKey)) { $isAutoKey = true; $keys[$keyName] = $autoKey; } else if($keyName == 'image_key') { $searchImageKey = $this->searchForImageKey($firstRssElement); if (!empty($searchImageKey)){ $keys[$keyName] = $searchImageKey; } } } if ($isShowDebug == true) { $title = UniteFunctionsUC::getVal($keyValue, "title"); $key = UniteFunctionsUC::getVal($keys, $keyName); $autoText = $isAutoKey?" <span style='color:grey;'>[auto detect]</span>":""; if(!array_key_exists($keyName, $keys)){ dmp("{$title}: <span style='color:darkred;'>not detected, please select a custom one</span>"); } else if (array_key_exists($keys[$keyName], $firstRssElement)) dmp("{$title}: <b>$key</b> {$autoText}"); else dmp("{$title}: <span style='color:darkred;'> key not found, please select another one</span>"); } } if ($isShowDebug) { dmp('--- first RSS item structure ---'); $firstRssShow = UniteFunctionsUC::modifyDataArrayForShow($firstRssElement); dmp($firstRssShow); } //set original date key if(isset($firstRssElement["publish_date_original"])) $keys["date_key_original"] = "publish_date_original"; else if(isset($firstRssElement["publish"])) $keys["date_key_original"] = "publish"; else $keys["date_key_original"] = "publish_date"; return $keys; } /** * Auto detect keys for rss widget */ private function autoDetectKeys($keyName, $rssElement) { if(array_key_exists($keyName, $this->rssAutoDetectKeys) == false) return(null); $autoDetectKey = $this->rssAutoDetectKeys[$keyName]; $arrKeys = explode('|', $autoDetectKey); foreach ($arrKeys as $key) { if(array_key_exists($key, $rssElement)) return($key); } return(null); } /** * Search for image key in an array of values */ public function searchForImageKey($rssItem) { $pattern = '/^https?:\/\/.*\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i'; foreach ($rssItem as $key => $value) { if (is_array($value)) { $newKey = $this->searchForImageKey($value); if (!empty($newKey)) { return $key . "." . $newKey; } } else if(preg_match($pattern, $value)) { return $key; } } return null; } /** * Check if exist any possible image keys or a key with image link */ private function hasImageKey($rssItem) { $possibleImageKeys = UniteFunctionsUC::getVal($this->rssAutoDetectKeys, "image_key"); if(empty($possibleImageKeys)) return(false); $rssKeys = explode('|', $possibleImageKeys); if(empty($rssKeys) == false) return(false); foreach ($rssKeys as $rssKey) { if(array_key_exists($rssKey, $rssItem)) { return true; } } $otherImageKey = $this->searchForImageKey($rssItem); if (!empty($otherImageKey)) { return true; } return false; } /** * Find first image in possible content keys or any other image */ private function getFirstImageLinkFromContent($rssItem) { $possibleContentKeys = $this->rssAutoDetectKeys['content_key']; $possibleDescKeys = $this->rssAutoDetectKeys['description_key']; $contentKeys = explode('|', $possibleContentKeys); $descKeys = explode('|', $possibleDescKeys); $rssKeys = array_merge($contentKeys, $descKeys); //$pattern = '/<img[^>]+src=["\']([^"\']+)["\']/i'; $pattern = '/<img\b[^>]*\bsrc\s*=\s*["\']([^"\']+)["\'][^>]*>/i'; foreach ($rssKeys as $rssKey) { if(array_key_exists($rssKey, $rssItem)) { if (!is_array($rssItem[$rssKey])) { $content = $rssItem[$rssKey]; if (preg_match($pattern, $content, $matches)) { if (!empty($matches[1])) { return $matches[1]; } } } else { $firstImage = $this->getFirstImageLinkFromContent($rssItem[$rssKey]); if (!empty($firstImage)) { return $firstImage; } } } } return null; } }