setPlugin($arlima_plugin); } /** * @param Arlima_Plugin $arlima_plugin */ function setPlugin($arlima_plugin) { $settings = $arlima_plugin->loadSettings(); $this->imported_lists = !empty($settings['imported_lists']) ? $settings['imported_lists']:array(); $this->arlima_plugin = $arlima_plugin; } /** * @return array */ function getImportedLists() { return $this->imported_lists; } /** * @param string $url */ function removeImportedList($url) { if(isset($this->imported_lists[$url])) { unset($this->imported_lists[$url]); $this->saveImportedLists(); } } /** * This function is used to register an external list as an imported list, * if you only want to fetch content from an external list use Arlima_ImportManager::loadListContent() * @param string $url * @param bool $refresh[optional=true] * @return Arlima_List */ function importList($url, $refresh=true) { if($refresh || empty($this->imported_lists[$url])) { $response = $this->loadExternalURL($url); $list = $this->serverResponseToArlimaList($response, $url); // will validate the response for us $this->imported_lists[$url] = array( 'title' => $list->title, 'url' => $url ); $this->saveImportedLists(); return $list; } else { return $this->imported_lists[$url]; } } /** * @param string $url * @return array|WP_Error */ protected function loadExternalURL($url) { if (!class_exists('WP_Http')) require_once ABSPATH . '/wp-includes/class-http.php'; $http = new WP_Http(); $response = $http->get($url); return $response; } /** */ private function saveImportedLists() { $settings = $this->arlima_plugin->loadSettings(); $settings['imported_lists'] = $this->imported_lists; $this->arlima_plugin->saveSettings($settings); } /** * @param string $url * @return Arlima_List */ function loadListContent($url) { return $this->serverResponseToArlimaList($this->loadExternalURL($url), $url); } /** * @param $response * @return null|string */ private function getResponseType($response) { $type = null; if(strpos($response['headers']['content-type'], 'json') !== false) { $type = 'json'; } elseif(strpos($response['headers']['content-type'], 'rss') !== false || strpos($response['headers']['content-type'], 'text/xml') !== false) { $type = 'rss'; } return $type; } /** * @param mixed $response * @param string $url * @throws Exception * @return Arlima_List */ private function serverResponseToArlimaList($response, $url) { // Validate response if($response instanceof WP_Error) { throw new Exception($response->get_error_message()); } $response_type = $this->getResponseType($response); if($response_type === null) { throw new Exception('Remote server did not respond with neither JSON nor RSS? got content-type: '.$response['headers']['content-type']); } if($response['response']['code'] != 200) { $list_data = @json_decode($response['body'], true); $error_message = $list_data && isset($list_data['error']) ? $list_data['error']:$response['body']; throw new Exception('Remote server responded with error: '.$error_message.' (status '.$response['response']['code'].')'); } // Parse response $list_data = $this->parseListData($response['body'], $response_type); // Populate the imported list $list = new Arlima_List(); foreach($list_data as $prop => $val) { if(property_exists($list, $prop)) { if($val instanceof stdClass) $val = (array)$val; $list->$prop = $val; } } $base_url = str_replace(array('http://', 'www.'), '', $url); $base_url = substr($base_url, 0, strpos($base_url, '/')); $list->is_imported = true; $list->exists = true; $list->id = $url; $list->title = '['.$base_url.'] '.$list->title; return $list; } /** * @param string $str * @param string $response_type * @return array|mixed * @throws Exception */ private function parseListData($str, $response_type) { $list_data = array(); // JSON DATA if($response_type == 'json') { $list_data = @json_decode($str, true); if (!$list_data) { throw new Exception('Unable to parse json. json error: ' . self::getLastJSONErrorMessage()); } if (empty($list_data['title']) || empty($list_data['slug']) || !isset($list_data['articles'])) { throw new Exception('JSON data invalid. Properties "title", "slug" and "articles" is mandatory'); } } // RSS DATA else { libxml_use_internal_errors(true); $xml = simplexml_load_string($str); if(!$xml) { throw new Exception('Unable to parse xml'); // todo: display what error that was thrown internally } if(empty($xml->channel) || empty($xml->channel->title) || empty($xml->channel->item)) { throw new Exception('Not a valid rss format, could not find title nor items'); } $pub_date = isset($xml->channel->pubDate) ? (string)$xml->channel->pubDate:(string)$xml->channel->lastBuildDate; $list_data = array( 'title' => (string)$xml->channel->title, 'slug' => sanitize_title((string)$xml->channel->title), 'articles' => array(), 'versions' => array(), 'version' => array('id'=>0, 'user_id'=>0, 'created' => self::rssPubDateToTime($pub_date)) ); if( !empty($xml->channel->item) ) { foreach($xml->channel->item as $item) { $guid = (string)$item->guid; $list_data['articles'][$guid] = $this->itemNodeToArticle($item); if($list_data['articles'][$guid]['publish_date'] > $list_data['version']['created']) { // ... when people don't really care about the RSS specs $list_data['version']['created'] = $list_data['articles'][$guid]['publish_date']; } } } } return $list_data; } /** * @param SimpleXMLElement|stdClass $item * @return array */ private function itemNodeToArticle($item) { $img_options = array(); // get image from node if(isset($item->image)) { $img_options = $this->generateArticleImageOptions((string)$item->image); } // get image from enclosure elseif(isset($item->enclosure) && $this->isEnlcosureValidImage($item->enclosure)) { $img_options = $this->generateArticleImageOptions((string)$item->enclosure->attributes()->url); } // Try to find an image in the description else { preg_match('/]+\>/i', (string)$item->description, $matches); if(isset($matches[0])) { preg_match('/src="([^"]*)"/i', $matches[0], $src); if(isset($src[1])) { $source = trim($src[1]); $ext = strtolower( substr($source, -4) ); if($ext == '.jpg' || $ext == 'jpeg' || $ext == '.png' || $ext == '.gif') { $img_options = $this->generateArticleImageOptions($source); } } } } // description cleanup $description = strip_tags((string)$item->description, '
');
        $description = force_balance_tags('

'.trim($description).'

', true); $description = $description == '

' ? '

...

':str_replace(array('"', '”'), '"', $description); $post_date = self::rssPubDateToTime( (string)$item->pubDate ); return Arlima_ListFactory::createArticleDataArray(array( 'url' => (string)$item->link, 'image_options' => $img_options, 'text' => $description, 'title' => (string)$item->title, 'publish_date' => $post_date, 'html_title' => '

'.( (string)$item->title ).'

' )); } /** * @param string $pubDate * @return int */ private static function rssPubDateToTime($pubDate) { return strtotime($pubDate) + (get_option( 'gmt_offset' ) * 3600); } /** * @param string $src * @return array */ private function generateArticleImageOptions($src) { return array( 'url' => $src, 'html' => '', 'image_class' => 'attachment', 'image_size' => 'large', 'attach_id' => 0 ); } /** * @param SimpleXMLElement|stdClass $enc * @return bool */ private function isEnlcosureValidImage($enc) { $attr = $enc->attributes(); return isset($attr->type) && in_array(strtolower($attr->type), array('image/jpg', 'image/jpeg', 'image/gif', 'image/png')); } /** * @static * @return string */ private static function getLastJSONErrorMessage() { switch (json_last_error()) { case JSON_ERROR_NONE: return ' - No errors'; break; case JSON_ERROR_DEPTH: return ' - Maximum stack depth exceeded'; break; case JSON_ERROR_STATE_MISMATCH: return ' - Underflow or the modes mismatch'; break; case JSON_ERROR_CTRL_CHAR: return ' - Unexpected control character found'; break; case JSON_ERROR_SYNTAX: return ' - Syntax error, malformed JSON'; break; case JSON_ERROR_UTF8: return ' - Malformed UTF-8 characters, possibly incorrectly encoded'; break; default: return ' - Unknown error'; break; } } /** * Helper function that displays info and remove button * for an imported list * @param string $url * @param string $name */ public static function displayImportedList($url, $name) { ?>