diff --git a/README.md b/README.md index 71269af..76d7fe8 100644 --- a/README.md +++ b/README.md @@ -190,14 +190,14 @@ try { 'langPath' => $_SERVER['DOCUMENT_ROOT'].'/inwidget/langs/', ); - $inWidget = new \inWidget\Core($config); + $inWidget = new \InWidget\Core($config); // Also, you may change default values of properties /* $inWidget->width = 800; // widget width in pixels $inWidget->inline = 6; // number of images in single line - $inWidget->view = 18; // number of images in widget + $inWidget->view = 18; // number of images in widget $inWidget->toolbar = false; // show profile avatar, statistic and action button $inWidget->preview = 'large'; // quality of images: small, large, fullsize $inWidget->adaptive = false; // enable adaptive mode diff --git a/classes/Autoload.php b/classes/Autoload.php index 592bf06..1e86ffe 100644 --- a/classes/Autoload.php +++ b/classes/Autoload.php @@ -1,14 +1,16 @@ account) && $this->account['username'] === $login) { - return $this->account; - } - $account = ''; - $answer = Request::get('https://api.instagram.com/v1/users/self/?access_token='.$token); - $this->checkAnswer($answer,'getAccountByLogin'); - $this->account = $this->prepareAccountData($answer->body->data); - return $this->account; - } - - /** - * Get media data by login - * - * @param string $login - * @param string $token - access token - * @param int $count - maximum medias per page [optional] - * @param int $maxId - return media earlier than this max_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByLogin($login, $token, $count = 30, $maxId = '') - { - $index = 0; - $medias = []; - $isMoreAvailable = true; - while ($index < $count && $isMoreAvailable) { - $answer = Request::get('https://api.instagram.com/v1/users/self/media/recent/?access_token='.$token.'&max_id='.$maxId); - $this->checkAnswer($answer); - $nodes = $answer->body->data; - if (empty($nodes)) { - return []; - } - foreach ($nodes as $item) { - if ($index === $count) { - return $this->prepareMediasData($medias); - } - $medias[] = $item; - $index++; - } - $maxId = $nodes[count($nodes) - 1]->id; - if(!isset($answer->body->pagination->next_url)) $isMoreAvailable = false; - } - return $this->prepareMediasData($medias); - } - - /** - * Get tagged media - * - * @param string $tag - * @param string $token - access token - * @param int $count - maximum medias per page [optional] - * @param string $maxId - return media earlier than this max_tag_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByTag($tag, $token, $count = 30, $maxId = '') - { - $index = 0; - $medias = []; - $isMoreAvailable = true; - $tag = parent::prepareTag($tag); - while ($index < $count && $isMoreAvailable) { - $answer = Request::get('https://api.instagram.com/v1/tags/'.urlencode($tag).'/media/recent/?access_token='.$token.'&max_tag_id='.$maxId); - $this->checkAnswer($answer); - $nodes = $answer->body->data; - if (empty($nodes)) { - return $this->prepareMediasData($medias); - } - foreach ($nodes as $item) { - if ($index === $count) { - return $this->prepareMediasData($medias); - } - $medias[] = $item; - $index++; - } - $maxId = $answer->body->pagination->next_max_tag_id; - if(!isset($answer->body->pagination->next_url)) $isMoreAvailable = false; - } - return $this->prepareMediasData($medias); - } - - /** - * Get tagged media from account - * - * @param string $tag - * @param string $login - * @param string $token - access token - * @param int $count - maximum medias per page [optional] - * @param string $maxId - return media earlier than this max_tag_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByTagFromAccount($tag, $login, $token = '', $count = 30, $maxId = '') - { - $tag = parent::prepareTag($tag); - $medias = $this->getMediasByLogin($login, $token, $count, $maxId); - $result = []; - foreach ($medias as $key=>$item) { - if(preg_match("/#".$tag."/is", $item['text'])) { - $result[] = $item; - } - } - return $result; - } - - /** - * Get account data independent of API names policy - * - * @param object $account - * @return array - */ - protected function prepareAccountData($account) - { - $data = []; - $data['userid'] = $account->id; - $data['username'] = $account->username; - $data['avatar'] = $account->profile_picture; - $data['full_name'] = $account->full_name; - $data['bio'] = $account->bio; - $data['website'] = $account->website; - $data['posts'] = $account->counts->media; - $data['followers'] = $account->counts->followed_by; - $data['following'] = $account->counts->follows; - return $data; - } - - /** - * Get media data independent of API names policy - * - * @param object $medias - * @return array - */ - protected function prepareMediasData($medias) - { - $data = []; - foreach ($medias as $key=>$item) { - $data[$key]['id'] = $this->ejectMediaId($item->id); - $data[$key]['code'] = $this->getCodeFromUrl($item->link); - $data[$key]['created'] = $item->created_time; - $data[$key]['text'] = $item->caption->text; - $data[$key]['link'] = $item->link; - $data[$key]['type'] = $item->type; - $data[$key]['fullsize'] = $item->images->standard_resolution->url; - $data[$key]['large'] = $item->images->low_resolution->url; - $data[$key]['small'] = $item->images->thumbnail->url; - $data[$key]['likesCount'] = $item->likes->count; - $data[$key]['commentsCount'] = $item->comments->count; - $data[$key]['authorId'] = $item->user->id; - } - return $data; - } - - /** - * Get media ID from combinated data returned by API - * - * @param string $id - * @return string - */ - private function ejectMediaId($id) - { - $id = explode('_', $id); - return $id[0]; - } - - /** - * Get media code from URL - * - * @param string $url - * @return string - */ - private function getCodeFromUrl($url) - { - preg_match('#.*\/p\/(.*)/#i', $url, $matches); - return $matches[1]; - } - - /** - * Check server response - * - * @param object $answer - returned by unirest-php library - * @param string $from - expected content type [to specify check] - * @return null - * @throws Exception - */ - public function checkAnswer($answer, $from = '') - { - if(!is_object($answer)) { - throw new \Exception('Unknown error. Server answer: '.$answer); - } - if($answer->code == 400) { - throw new \Exception('Invalid ACCESS TOKEN. Server answer: '.$answer->raw_body); - } - if($answer->code == 429) { - throw new \Exception('The maximum number of requests per hour has been exceeded.'); - } - if($answer->code !== 200) { - throw new \Exception('Unknown error. Server answer: '.$answer->raw_body); - } - if($from === 'getAccountByLogin') { - if(empty($answer->body->data)) { - throw new \Exception('Account with given username does not exist or not available in sandbox mode.'); - } - } - } - -} \ No newline at end of file diff --git a/classes/InWidget/API/apiScraper.php b/classes/InWidget/API/apiScraper.php deleted file mode 100644 index 50fbbdb..0000000 --- a/classes/InWidget/API/apiScraper.php +++ /dev/null @@ -1,158 +0,0 @@ -api = new \InstagramScraper\Instagram(); - if(!empty($login) AND !empty($password)) { - $api = $this->api->withCredentials($login, $password); - $api->login(); - $this->api = $api; - } - } - - /** - * Get account data by login - * - * @param string $login - * @param string $token - fake param, not needed to this API driver - * @return array - * @throws Exception - */ - public function getAccountByLogin($login, $token='') - { - $account = $this->api->getAccount($login); - if($account->isPrivate()) { - throw new \Exception('Requested profile is private'); - } - return $this->prepareAccountData($account); - } - - /** - * Get media data by login - * - * @param string $login - * @param string $token - fake param, not needed to this API driver - * @param int $count - maximum medias per page [optional] - * @param int $maxId - return media earlier than this max_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByLogin($login, $token = '', $count = 30, $maxId = '') - { - $medias = $this->api->getMedias($login, $count, $maxId); - return $this->prepareMediasData($medias); - } - - /** - * Get tagged media - * - * @param string $tag - * @param string $token - fake param, not needed to this API driver - * @param int $count - maximum medias per page [optional] - * @param string $maxId - return media earlier than this max_tag_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByTag($tag, $token = '', $count = 30, $maxId = '') - { - $tag = parent::prepareTag($tag); - $medias = $this->api->getMediasByTag($tag, $count, $maxId); - return $this->prepareMediasData($medias); - } - - /** - * Get tagged media from account - * - * @param string $tag - * @param string $login - * @param string $token - fake param, not needed to this API driver - * @param int $count - maximum medias per page [optional] - * @param string $maxId - return media earlier than this max_id [optional] - * @return array - * @throws Exception - */ - public function getMediasByTagFromAccount($tag, $login, $token = '', $count = 30, $maxId = '') - { - $tag = parent::prepareTag($tag); - $medias = $this->getMediasByLogin($login, $token, $count, $maxId); - $result = []; - foreach ($medias as $key=>$item) { - if(preg_match("/#".$tag."/is", $item['text'])) { - $result[] = $item; - } - } - return $result; - } - - /** - * Get account data independent of API names policy - * - * @param object $account - * @return array - */ - protected function prepareAccountData($account) - { - $data = []; - $data['userid'] = $account->getId(); - $data['username'] = $account->getUsername(); - $data['avatar'] = $account->getProfilePicUrl(); - $data['full_name'] = $account->getFullName(); - $data['bio'] = $account->getBiography(); - $data['website'] = $account->getExternalUrl(); - $data['posts'] = $account->getMediaCount(); - $data['followers'] = $account->getFollowedByCount(); - $data['following'] = $account->getFollowsCount(); - return $data; - } - - /** - * Get media data independent of API names policy - * - * @param object $medias - * @return array - */ - protected function prepareMediasData($medias) - { - $data = []; - foreach ($medias as $key=>$item) { - $data[$key]['id'] = $item->getId(); - $data[$key]['code'] = $item->getShortCode(); - $data[$key]['created'] = $item->getCreatedTime(); - $data[$key]['text'] = $item->getCaption(); - $data[$key]['link'] = $item->getLink(); - $data[$key]['type'] = $item->getType(); - $data[$key]['fullsize'] = $item->getImageHighResolutionUrl(); - $data[$key]['large'] = $item->getImageStandardResolutionUrl(); - $data[$key]['small'] = $item->getImageLowResolutionUrl(); - $data[$key]['likesCount'] = $item->getLikesCount(); - $data[$key]['commentsCount'] = $item->getCommentsCount(); - $data[$key]['authorId'] = $item->getOwnerId(); - if(empty($data[$key]['type'])) { - $data[$key]['type'] = "image"; - } - } - return $data; - } - -} \ No newline at end of file diff --git a/classes/InWidget/Api/ApiModel.php b/classes/InWidget/Api/ApiModel.php new file mode 100644 index 0000000..005750c --- /dev/null +++ b/classes/InWidget/Api/ApiModel.php @@ -0,0 +1,121 @@ +account) && $this->account['username'] === $login) { + return $this->account; + } + $account = ''; + $answer = Request::get('https://api.instagram.com/v1/users/self/?access_token=' . $token); + $this->checkAnswer($answer, 'getAccountByLogin'); + $this->account = $this->prepareAccountData($answer->body->data); + return $this->account; + } + + /** + * Get media data by login + * + * @param string $login + * @param string $token - access token + * @param int $count - maximum medias per page [optional] + * @param int $maxId - return media earlier than this max_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByLogin($login, $token, $count = 30, $maxId = '') + { + $index = 0; + $medias = []; + $isMoreAvailable = true; + while ($index < $count && $isMoreAvailable) { + $answer = Request::get( + 'https://api.instagram.com/v1/users/self/media/recent/?access_token=' . $token . '&max_id=' . $maxId + ); + $this->checkAnswer($answer); + $nodes = $answer->body->data; + if (empty($nodes)) { + return []; + } + foreach ($nodes as $item) { + if ($index === $count) { + return $this->prepareMediasData($medias); + } + $medias[] = $item; + $index++; + } + $maxId = $nodes[count($nodes) - 1]->id; + if (!isset($answer->body->pagination->next_url)) { + $isMoreAvailable = false; + } + } + return $this->prepareMediasData($medias); + } + + /** + * Get tagged media + * + * @param string $tag + * @param string $token - access token + * @param int $count - maximum medias per page [optional] + * @param string $maxId - return media earlier than this max_tag_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByTag($tag, $token, $count = 30, $maxId = '') + { + $index = 0; + $medias = []; + $isMoreAvailable = true; + $tag = parent::prepareTag($tag); + while ($index < $count && $isMoreAvailable) { + $answer = Request::get( + 'https://api.instagram.com/v1/tags/' . urlencode( + $tag + ) . '/media/recent/?access_token=' . $token . '&max_tag_id=' . $maxId + ); + $this->checkAnswer($answer); + $nodes = $answer->body->data; + if (empty($nodes)) { + return $this->prepareMediasData($medias); + } + foreach ($nodes as $item) { + if ($index === $count) { + return $this->prepareMediasData($medias); + } + $medias[] = $item; + $index++; + } + $maxId = $answer->body->pagination->next_max_tag_id; + if (!isset($answer->body->pagination->next_url)) { + $isMoreAvailable = false; + } + } + return $this->prepareMediasData($medias); + } + + /** + * Get tagged media from account + * + * @param string $tag + * @param string $login + * @param string $token - access token + * @param int $count - maximum medias per page [optional] + * @param string $maxId - return media earlier than this max_tag_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByTagFromAccount($tag, $login, $token = '', $count = 30, $maxId = '') + { + $tag = parent::prepareTag($tag); + $medias = $this->getMediasByLogin($login, $token, $count, $maxId); + $result = []; + foreach ($medias as $key => $item) { + if (preg_match("/#" . $tag . "/is", $item['text'])) { + $result[] = $item; + } + } + return $result; + } + + /** + * Get account data independent of API names policy + * + * @param object $account + * @return array + */ + protected function prepareAccountData($account) + { + $data = []; + $data['userid'] = $account->id; + $data['username'] = $account->username; + $data['avatar'] = $account->profile_picture; + $data['full_name'] = $account->full_name; + $data['bio'] = $account->bio; + $data['website'] = $account->website; + $data['posts'] = $account->counts->media; + $data['followers'] = $account->counts->followed_by; + $data['following'] = $account->counts->follows; + return $data; + } + + /** + * Get media data independent of API names policy + * + * @param object $medias + * @return array + */ + protected function prepareMediasData($medias) + { + $data = []; + foreach ($medias as $key => $item) { + $data[$key]['id'] = $this->ejectMediaId($item->id); + $data[$key]['code'] = $this->getCodeFromUrl($item->link); + $data[$key]['created'] = $item->created_time; + $data[$key]['text'] = $item->caption->text; + $data[$key]['link'] = $item->link; + $data[$key]['type'] = $item->type; + $data[$key]['fullsize'] = $item->images->standard_resolution->url; + $data[$key]['large'] = $item->images->low_resolution->url; + $data[$key]['small'] = $item->images->thumbnail->url; + $data[$key]['likesCount'] = $item->likes->count; + $data[$key]['commentsCount'] = $item->comments->count; + $data[$key]['authorId'] = $item->user->id; + } + return $data; + } + + /** + * Get media ID from combinated data returned by API + * + * @param string $id + * @return string + */ + private function ejectMediaId($id) + { + $id = explode('_', $id); + return $id[0]; + } + + /** + * Get media code from URL + * + * @param string $url + * @return string + */ + private function getCodeFromUrl($url) + { + preg_match('#.*\/p\/(.*)/#i', $url, $matches); + return $matches[1]; + } + + /** + * Check server response + * + * @param object $answer - returned by unirest-php library + * @param string $from - expected content type [to specify check] + * @return null + * @throws \Exception + */ + public function checkAnswer($answer, $from = '') + { + if (!is_object($answer)) { + throw new \Exception('Unknown error. Server answer: ' . $answer); + } + if ($answer->code == 400) { + throw new \Exception('Invalid ACCESS TOKEN. Server answer: ' . $answer->raw_body); + } + if ($answer->code == 429) { + throw new \Exception('The maximum number of requests per hour has been exceeded.'); + } + if ($answer->code !== 200) { + throw new \Exception('Unknown error. Server answer: ' . $answer->raw_body); + } + if ($from === 'getAccountByLogin') { + if (empty($answer->body->data)) { + throw new \Exception('Account with given username does not exist or not available in sandbox mode.'); + } + } + } +} diff --git a/classes/InWidget/Api/ApiScraper.php b/classes/InWidget/Api/ApiScraper.php new file mode 100644 index 0000000..283ddf8 --- /dev/null +++ b/classes/InWidget/Api/ApiScraper.php @@ -0,0 +1,157 @@ +api = new \InstagramScraper\Instagram(); + if (!empty($login) and !empty($password)) { + $api = $this->api->withCredentials($login, $password); + $api->login(); + $this->api = $api; + } + } + + /** + * Get account data by login + * + * @param string $login + * @param string $token - fake param, not needed to this API driver + * @return array + * @throws \Exception + */ + public function getAccountByLogin($login, $token = '') + { + $account = $this->api->getAccount($login); + if ($account->isPrivate()) { + throw new \Exception('Requested profile is private'); + } + return $this->prepareAccountData($account); + } + + /** + * Get media data by login + * + * @param string $login + * @param string $token - fake param, not needed to this API driver + * @param int $count - maximum medias per page [optional] + * @param int $maxId - return media earlier than this max_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByLogin($login, $token = '', $count = 30, $maxId = '') + { + $medias = $this->api->getMedias($login, $count, $maxId); + return $this->prepareMediasData($medias); + } + + /** + * Get tagged media + * + * @param string $tag + * @param string $token - fake param, not needed to this API driver + * @param int $count - maximum medias per page [optional] + * @param string $maxId - return media earlier than this max_tag_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByTag($tag, $token = '', $count = 30, $maxId = '') + { + $tag = parent::prepareTag($tag); + $medias = $this->api->getMediasByTag($tag, $count, $maxId); + return $this->prepareMediasData($medias); + } + + /** + * Get tagged media from account + * + * @param string $tag + * @param string $login + * @param string $token - fake param, not needed to this API driver + * @param int $count - maximum medias per page [optional] + * @param string $maxId - return media earlier than this max_id [optional] + * @return array + * @throws \Exception + */ + public function getMediasByTagFromAccount($tag, $login, $token = '', $count = 30, $maxId = '') + { + $tag = parent::prepareTag($tag); + $medias = $this->getMediasByLogin($login, $token, $count, $maxId); + $result = []; + foreach ($medias as $key => $item) { + if (preg_match("/#" . $tag . "/is", $item['text'])) { + $result[] = $item; + } + } + return $result; + } + + /** + * Get account data independent of API names policy + * + * @param object $account + * @return array + */ + protected function prepareAccountData($account) + { + $data = []; + $data['userid'] = $account->getId(); + $data['username'] = $account->getUsername(); + $data['avatar'] = $account->getProfilePicUrl(); + $data['full_name'] = $account->getFullName(); + $data['bio'] = $account->getBiography(); + $data['website'] = $account->getExternalUrl(); + $data['posts'] = $account->getMediaCount(); + $data['followers'] = $account->getFollowedByCount(); + $data['following'] = $account->getFollowsCount(); + return $data; + } + + /** + * Get media data independent of API names policy + * + * @param object $medias + * @return array + */ + protected function prepareMediasData($medias) + { + $data = []; + foreach ($medias as $key => $item) { + $data[$key]['id'] = $item->getId(); + $data[$key]['code'] = $item->getShortCode(); + $data[$key]['created'] = $item->getCreatedTime(); + $data[$key]['text'] = $item->getCaption(); + $data[$key]['link'] = $item->getLink(); + $data[$key]['type'] = $item->getType(); + $data[$key]['fullsize'] = $item->getImageHighResolutionUrl(); + $data[$key]['large'] = $item->getImageStandardResolutionUrl(); + $data[$key]['small'] = $item->getImageLowResolutionUrl(); + $data[$key]['likesCount'] = $item->getLikesCount(); + $data[$key]['commentsCount'] = $item->getCommentsCount(); + $data[$key]['authorId'] = $item->getOwnerId(); + if (empty($data[$key]['type'])) { + $data[$key]['type'] = "image"; + } + } + return $data; + } +} diff --git a/classes/InWidget/Core.php b/classes/InWidget/Core.php index 5018fae..7ecf62d 100644 --- a/classes/InWidget/Core.php +++ b/classes/InWidget/Core.php @@ -1,9 +1,9 @@ config = $config; - } - else { - require_once 'config.php'; - $this->config = $CONFIG; - } - $this->checkConfig(); - $this->checkCacheRights(); - $this->setLang(); - $this->setSkin(); - $this->setOptions(); - try { - if(!empty($this->config['ACCESS_TOKEN'])) { - $this->api = apiModel::getInstance('official'); - } - else { - $this->api = apiModel::getInstance('', $config['authLogin'], $config['authPassword']); - } - } - catch (\Exception $e) { - throw new inWidgetException($e->getMessage(), 500, $this->getCacheFilePath()); - } - } - /** - * Send request to Instagram - * - * @return null - * @throws inWidgetException - */ - private function apiQuery() - { - try { - $this->account = $this->api->getAccountByLogin($this->config['LOGIN'], $this->config['ACCESS_TOKEN']); - // by hashtag - if(!empty($this->config['HASHTAG'])) { - $mediaArray = []; - $tags = explode(',', $this->config['HASHTAG']); - if(!empty($tags)) { - foreach ($tags as $key=>$item) { - if(!empty($item)) { - if($this->config['tagsFromAccountOnly'] === true) { - $mediaArray[] = $this->api->getMediasByTagFromAccount($item, $this->config['LOGIN'], $this->config['ACCESS_TOKEN'], $this->config['imgCount']); - } - else { - $mediaArray[] = $this->api->getMediasByTag($item, $this->config['ACCESS_TOKEN'], $this->config['imgCount']); - } - } - } - } - $medias = []; - if(!empty($mediaArray)) { - foreach ($mediaArray as $key=>$item){ - $medias = array_merge($medias, $item); - } - } - $this->medias = $medias; - unset($mediaArray,$medias); - } - // by profile - else { - $this->medias = $this->api->getMediasByLogin($this->config['LOGIN'], $this->config['ACCESS_TOKEN'], $this->config['imgCount']); - } - } catch (\Exception $e) { - throw new inWidgetException($e->getMessage(), 500, $this->getCacheFilePath()); - } - // Get banned ids. Ignore any errors - if(!empty($this->config['tagsBannedLogins'])) { - foreach ($this->config['tagsBannedLogins'] as $key=>$item) { - try { - $banned = $this->api->getAccountByLogin($item['login'], $this->config['ACCESS_TOKEN']); - $this->config['tagsBannedLogins'][$key]['id'] = $banned['userid']; - } catch (\Exception $e) {} - } - $this->banned = $this->config['tagsBannedLogins']; - } - } - /** - * Get data from Instagram (or actual cache) - * - * @return object - * @throws Exception - * @throws inWidgetException - */ - public function getData() - { - $this->data = $this->getCache(); - if(empty($this->data)) { - $this->apiQuery(); - $this->createCache(); - $this->data = json_decode(file_get_contents($this->getCacheFilePath())); - } - if(!is_object($this->data)) { - $this->data = $this->getBackup(); - if(!is_object($this->data)) { - $this->data = $this->getCache(); - throw new \Exception('Cache file contains plain text:
'.$this->data); - } - else $this->data->isBackup = true; - } - return $this->data; - } - /** - * Get data independent of API names policy - * @return array - */ - private function prepareData() - { - $data = $this->account; - $data['banned'] = $this->banned; - $data['tags'] = $this->config['HASHTAG']; - $data['images'] = $this->medias; - $data['lastupdate'] = time(); - return $data; - } - /** - * @return mixed - * @throws inWidgetException - */ - private function getCache() - { - if($this->config['cacheSkip'] === true) { - return false; - } - $mtime = @filemtime($this->getCacheFilePath()); - if($mtime<=0) { - throw new inWidgetException('Can\'t get modification time of {$cacheFile}. Cache always be expired.', 102, $this->getCacheFilePath()); - } - $cacheExpTime = $mtime + ($this->config['cacheExpiration']*60*60); - if(time() > $cacheExpTime) return false; - else { - $rawData = file_get_contents($this->getCacheFilePath()); - $cacheData = json_decode($rawData); - if(!is_object($cacheData)) return $rawData; - unset($rawData); - } - return $cacheData; - } - /** - * @return mixed - */ - private function getBackup() { - $file = $this->getCacheFilePath().'_backup'; - if(file_exists($file)) { - $rawData = file_get_contents($file); - $cacheData = json_decode($rawData); - if(!is_object($cacheData)) return $rawData; - else return $cacheData; - } - } - /** - * @return null - */ - private function createCache() - { - $data = json_encode($this->prepareData()); - file_put_contents($this->getCacheFilePath(), $data, LOCK_EX); - file_put_contents($this->getCacheFilePath().'_backup', $data, LOCK_EX); - } - /** - * @return string - */ - public function getCacheFilePath() - { - return $this->cachePath.''.$this->cacheFile; - } - /** - * Check important values and prepare to work - * - * @return null - * @throws Exception - */ - private function checkConfig() - { - if(!empty($this->config['skinAvailable'])) { - $this->skinAvailable = $this->config['skinAvailable']; - } - if(!empty($this->config['langAvailable'])) { - $this->langAvailable = $this->config['langAvailable']; - } - if(!empty($this->config['loginAvailable'])) { - $this->loginAvailable = $this->config['loginAvailable']; - } - if(!empty($this->config['tagsAvailable'])) { - $this->tagsAvailable = $this->config['tagsAvailable']; - } - if(empty($this->config['LOGIN'])) { - throw new \Exception(__CLASS__.': LOGIN required in config.php'); - } - if(!in_array($this->config['langDefault'], $this->langAvailable, true)){ - throw new \Exception(__CLASS__.': default language does not present in "langAvailable" config property'); - } - if(!in_array($this->config['skinDefault'], $this->skinAvailable, true)){ - throw new \Exception(__CLASS__.': default skin does not present in "skinAvailable" config property'); - } - // prepare paths - $this->langPath = __DIR__.'/'.$this->langPath; // PHP < 5.6 fix - $this->cachePath = __DIR__.'/'.$this->cachePath; // PHP < 5.6 fix - // prepare login - if($this->skipGET === false) { - if(isset($_GET['login'])) { - if(in_array($_GET['login'], $this->loginAvailable)){ - $this->config['LOGIN'] = $_GET['login']; - // login priority by default tags - $this->config['HASHTAG'] = ""; - } - else { - throw new \Exception(__CLASS__.': login does not present in "loginAvailable" config property'); - } - } - } - $this->config['LOGIN'] = strtolower(trim($this->config['LOGIN'])); - $cacheFileName = md5($this->config['LOGIN']); - // prepare hashtags - if($this->skipGET === false) { - if(isset($_GET['tag'])) { - if(in_array($_GET['tag'], $this->tagsAvailable)){ - $this->config['HASHTAG'] = urldecode($_GET['tag']); - } - else { - throw new \Exception(__CLASS__.': tag does not present in "tagsAvailable" config property'); - } - } - } - if(!empty($this->config['HASHTAG'])) { - $this->config['HASHTAG'] = trim($this->config['HASHTAG']); - $this->config['HASHTAG'] = str_replace('#', '', $this->config['HASHTAG']); - $cacheFileName = md5($cacheFileName.$this->config['HASHTAG'].'_tags'); - } - if(!empty($this->config['skinPath'])) { - $this->skinPath = $this->config['skinPath']; - } - if(!empty($this->config['cachePath'])) { - $this->cachePath = $this->config['cachePath']; - } - if(!empty($this->config['langPath'])) { - $this->langPath = $this->config['langPath']; - } - $this->cacheFile = str_replace('{$fileName}', $cacheFileName, $this->cacheFile); - if(!empty($this->config['tagsBannedLogins'])) { - $logins = explode(',', $this->config['tagsBannedLogins']); - if(!empty($logins)) { - $this->config['tagsBannedLogins'] = []; - foreach ($logins as $key=>$item) { - $item = strtolower(trim($item)); - $this->config['tagsBannedLogins'][$key]['login'] = $item; - } - } - } - else $this->config['tagsBannedLogins'] = []; - } - /** - * Let me know if cache file not writable - * - * @return null - * @throws inWidgetException - */ - private function checkCacheRights() - { - $cacheFile = @fopen($this->getCacheFilePath(), 'a+b'); - if(!is_resource($cacheFile)) { - throw new inWidgetException('Can\'t get access to file {$cacheFile}. Check file path or permissions.', 101, $this->getCacheFilePath()); - } - fclose($cacheFile); - - } - /** - * Set widget lang - * New value must be present in langAvailable property necessary - * - * @param string $name [optional] - * @return null - */ - public function setLang($name = '') - { - if(empty($name) AND $this->config['langAuto'] === true AND !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) - $name = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); - if(!empty($name) AND in_array($name, $this->langAvailable, true)){ - $name = strtolower($name); - if(file_exists($this->langPath.$name.'.php')) { - $this->langName = $name; - require $this->langPath.$name.'.php'; - } - } - if(empty($LANG)) { - $this->langName = $this->config['langDefault']; - require $this->langPath.$this->config['langDefault'].'.php'; - } - $this->lang = $LANG; - } - /** - * Set widget skin - * New value must be present in skinAvailable property necessary - * - * @param string $name [optional] - * @return null - */ - public function setSkin($name = '') - { - if(!empty($name) AND in_array($name, $this->skinAvailable, true)){ - $this->skinName = $name; - } - else $this->skinName = $this->config['skinDefault']; - } - /** - * Set new values of properties through the $_GET - * - * @return null - */ - public function setOptions() - { - $this->width -= 2; - if($this->skipGET === false) { - if(isset($_GET['width']) AND (int)$_GET['width']>0) - $this->width = $_GET['width']-2; - if(isset($_GET['inline']) AND (int)$_GET['inline']>0) - $this->inline = $_GET['inline']; - if(isset($_GET['view']) AND (int)$_GET['view']>0) - $this->view = $_GET['view']; - if(isset($_GET['toolbar']) AND $_GET['toolbar'] == 'false' OR !empty($this->config['HASHTAG'])) - $this->toolbar = false; - if(isset($_GET['adaptive']) AND $_GET['adaptive'] == 'true') - $this->adaptive = true; - if(isset($_GET['preview'])) - $this->preview = $_GET['preview']; - if(isset($_GET['lang'])) - $this->setLang($_GET['lang']); - if(isset($_GET['skin'])) - $this->setSkin($_GET['skin']); - } - if($this->width>0) - $this->imgWidth = round(($this->width-(17+(9*$this->inline)))/$this->inline); - } - /** - * Let me know if this user was banned - * - * @param int $id - * @return bool - */ - public function isBannedUserId($id) - { - if(!empty($this->data->banned)) { - foreach ($this->data->banned as $key1=>$cacheValue) { - if(!empty($cacheValue->id) AND $cacheValue->id === $id) { - if(!empty($this->config['tagsBannedLogins'])) { - foreach ($this->config['tagsBannedLogins'] as $key2=>$configValue) { - if($configValue['login'] === $cacheValue->login) - return true; - } - } - } - } - } - return false; - } - /** - * Get number of images without images of banned users - * - * @param object $images - * @return int - */ - public function countAvailableImages($images) - { - $count = 0; - if(!empty($images)){ - foreach ($images as $key=>$item){ - if($this->isBannedUserId($item->authorId) == true) continue; - $count++; - } - } - return $count; - } -} \ No newline at end of file + public $config = []; + public $data = []; + public $api = false; + private $account = false; + private $medias = false; + private $banned = []; + public $width = 260; + public $inline = 4; + public $view = 12; + public $toolbar = true; + public $adaptive = false; + public $preview = 'large'; + public $imgWidth = 0; + public $skipGET = false; + public $loginAvailable = []; + public $tagsAvailable = []; + public $lang = []; + public $langName = ''; + public $langAvailable = ['ru', 'en', 'ua']; + private $langPath = 'langs/'; + private $cachePath = 'cache/'; + private $cacheFile = '{$fileName}.txt'; + public $skinName = 'default'; + public $skinPath = 'skins/'; + public $skinAvailable = [ + 'default', + 'modern-blue', + 'modern-green', + 'modern-red', + 'modern-orange', + 'modern-grey', + 'modern-black', + 'modern-violet', + 'modern-yellow', + ]; + + /** + * @param array $config [optional] - like config.php + * @return null + * @throws InWidgetException + */ + public function __construct($config = []) + { + if (!empty($config)) { + $this->config = $config; + } else { + require_once 'config.php'; + $this->config = $CONFIG; + } + $this->checkConfig(); + $this->checkCacheRights(); + $this->setLang(); + $this->setSkin(); + $this->setOptions(); + try { + if (!empty($this->config['ACCESS_TOKEN'])) { + $this->api = ApiModel::getInstance('official'); + } else { + $this->api = ApiModel::getInstance('', $config['authLogin'], $config['authPassword']); + } + } catch (\Exception $e) { + throw new InWidgetException($e->getMessage(), 500, $this->getCacheFilePath()); + } + } + + /** + * Send request to Instagram + * + * @return null + * @throws InWidgetException + */ + private function apiQuery() + { + try { + $this->account = $this->api->getAccountByLogin($this->config['LOGIN'], $this->config['ACCESS_TOKEN']); + // by hashtag + if (!empty($this->config['HASHTAG'])) { + $mediaArray = []; + $tags = explode(',', $this->config['HASHTAG']); + if (!empty($tags)) { + foreach ($tags as $key => $item) { + if (!empty($item)) { + if ($this->config['tagsFromAccountOnly'] === true) { + $mediaArray[] = $this->api->getMediasByTagFromAccount( + $item, + $this->config['LOGIN'], + $this->config['ACCESS_TOKEN'], + $this->config['imgCount'] + ); + } else { + $mediaArray[] = $this->api->getMediasByTag( + $item, + $this->config['ACCESS_TOKEN'], + $this->config['imgCount'] + ); + } + } + } + } + $medias = []; + if (!empty($mediaArray)) { + foreach ($mediaArray as $key => $item) { + $medias = array_merge($medias, $item); + } + } + $this->medias = $medias; + unset($mediaArray, $medias); + } else { + $this->medias = $this->api->getMediasByLogin( + $this->config['LOGIN'], + $this->config['ACCESS_TOKEN'], + $this->config['imgCount'] + ); + } + } catch (\Exception $e) { + throw new InWidgetException($e->getMessage(), 500, $this->getCacheFilePath()); + } + // Get banned ids. Ignore any errors + if (!empty($this->config['tagsBannedLogins'])) { + foreach ($this->config['tagsBannedLogins'] as $key => $item) { + try { + $banned = $this->api->getAccountByLogin($item['login'], $this->config['ACCESS_TOKEN']); + $this->config['tagsBannedLogins'][$key]['id'] = $banned['userid']; + } catch (\Exception $e) { + } + } + $this->banned = $this->config['tagsBannedLogins']; + } + } + + /** + * Get data from Instagram (or actual cache) + * + * @return object + * @throws \Exception + * @throws InWidgetException + */ + public function getData() + { + $this->data = $this->getCache(); + if (empty($this->data)) { + $this->apiQuery(); + $this->createCache(); + $this->data = json_decode(file_get_contents($this->getCacheFilePath())); + } + if (!is_object($this->data)) { + $this->data = $this->getBackup(); + if (!is_object($this->data)) { + $this->data = $this->getCache(); + throw new \Exception('Cache file contains plain text:
' . $this->data); + } else { + $this->data->isBackup = true; + } + } + return $this->data; + } + + /** + * Get data independent of API names policy + * @return array + */ + private function prepareData() + { + $data = $this->account; + $data['banned'] = $this->banned; + $data['tags'] = $this->config['HASHTAG']; + $data['images'] = $this->medias; + $data['lastupdate'] = time(); + return $data; + } + + /** + * @return mixed + * @throws InWidgetException + */ + private function getCache() + { + if ($this->config['cacheSkip'] === true) { + return false; + } + $mtime = @filemtime($this->getCacheFilePath()); + if ($mtime <= 0) { + throw new InWidgetException( + 'Can\'t get modification time of {$cacheFile}. Cache always be expired.', + 102, + $this->getCacheFilePath() + ); + } + $cacheExpTime = $mtime + ($this->config['cacheExpiration'] * 60 * 60); + if (time() > $cacheExpTime) { + return false; + } else { + $rawData = file_get_contents($this->getCacheFilePath()); + $cacheData = json_decode($rawData); + if (!is_object($cacheData)) { + return $rawData; + } + unset($rawData); + } + return $cacheData; + } + + /** + * @return mixed + */ + private function getBackup() + { + $file = $this->getCacheFilePath() . '_backup'; + if (file_exists($file)) { + $rawData = file_get_contents($file); + $cacheData = json_decode($rawData); + if (!is_object($cacheData)) { + return $rawData; + } else { + return $cacheData; + } + } + } + + /** + * @return null + */ + private function createCache() + { + $data = json_encode($this->prepareData()); + file_put_contents($this->getCacheFilePath(), $data, LOCK_EX); + file_put_contents($this->getCacheFilePath() . '_backup', $data, LOCK_EX); + } + + /** + * @return string + */ + public function getCacheFilePath() + { + return $this->cachePath . '' . $this->cacheFile; + } + + /** + * Check important values and prepare to work + * + * @return null + * @throws \Exception + */ + private function checkConfig() + { + if (!empty($this->config['skinAvailable'])) { + $this->skinAvailable = $this->config['skinAvailable']; + } + if (!empty($this->config['langAvailable'])) { + $this->langAvailable = $this->config['langAvailable']; + } + if (!empty($this->config['loginAvailable'])) { + $this->loginAvailable = $this->config['loginAvailable']; + } + if (!empty($this->config['tagsAvailable'])) { + $this->tagsAvailable = $this->config['tagsAvailable']; + } + if (empty($this->config['LOGIN'])) { + throw new \Exception(__CLASS__ . ': LOGIN required in config.php'); + } + if (!in_array($this->config['langDefault'], $this->langAvailable, true)) { + throw new \Exception(__CLASS__ . ': default language does not present in "langAvailable" config property'); + } + if (!in_array($this->config['skinDefault'], $this->skinAvailable, true)) { + throw new \Exception(__CLASS__ . ': default skin does not present in "skinAvailable" config property'); + } + // prepare paths + $this->langPath = __DIR__ . '/' . $this->langPath; // PHP < 5.6 fix + $this->cachePath = __DIR__ . '/' . $this->cachePath; // PHP < 5.6 fix + // prepare login + if ($this->skipGET === false) { + if (isset($_GET['login'])) { + if (in_array($_GET['login'], $this->loginAvailable)) { + $this->config['LOGIN'] = $_GET['login']; + // login priority by default tags + $this->config['HASHTAG'] = ""; + } else { + throw new \Exception(__CLASS__ . ': login does not present in "loginAvailable" config property'); + } + } + } + $this->config['LOGIN'] = strtolower(trim($this->config['LOGIN'])); + $cacheFileName = md5($this->config['LOGIN']); + // prepare hashtags + if ($this->skipGET === false) { + if (isset($_GET['tag'])) { + if (in_array($_GET['tag'], $this->tagsAvailable)) { + $this->config['HASHTAG'] = urldecode($_GET['tag']); + } else { + throw new \Exception(__CLASS__ . ': tag does not present in "tagsAvailable" config property'); + } + } + } + if (!empty($this->config['HASHTAG'])) { + $this->config['HASHTAG'] = trim($this->config['HASHTAG']); + $this->config['HASHTAG'] = str_replace('#', '', $this->config['HASHTAG']); + $cacheFileName = md5($cacheFileName . $this->config['HASHTAG'] . '_tags'); + } + if (!empty($this->config['skinPath'])) { + $this->skinPath = $this->config['skinPath']; + } + if (!empty($this->config['cachePath'])) { + $this->cachePath = $this->config['cachePath']; + } + if (!empty($this->config['langPath'])) { + $this->langPath = $this->config['langPath']; + } + $this->cacheFile = str_replace('{$fileName}', $cacheFileName, $this->cacheFile); + if (!empty($this->config['tagsBannedLogins'])) { + $logins = explode(',', $this->config['tagsBannedLogins']); + if (!empty($logins)) { + $this->config['tagsBannedLogins'] = []; + foreach ($logins as $key => $item) { + $item = strtolower(trim($item)); + $this->config['tagsBannedLogins'][$key]['login'] = $item; + } + } + } else { + $this->config['tagsBannedLogins'] = []; + } + } + + /** + * Let me know if cache file not writable + * + * @return null + * @throws InWidgetException + */ + private function checkCacheRights() + { + $cacheFile = @fopen($this->getCacheFilePath(), 'a+b'); + if (!is_resource($cacheFile)) { + throw new InWidgetException( + 'Can\'t get access to file {$cacheFile}. Check file path or permissions.', + 101, + $this->getCacheFilePath() + ); + } + fclose($cacheFile); + } + + /** + * Set widget lang + * New value must be present in langAvailable property necessary + * + * @param string $name [optional] + * @return null + */ + public function setLang($name = '') + { + if (empty($name) and $this->config['langAuto'] === true and !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $name = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); + } + if (!empty($name) and in_array($name, $this->langAvailable, true)) { + $name = strtolower($name); + if (file_exists($this->langPath . $name . '.php')) { + $this->langName = $name; + require $this->langPath . $name . '.php'; + } + } + if (empty($LANG)) { + $this->langName = $this->config['langDefault']; + require $this->langPath . $this->config['langDefault'] . '.php'; + } + $this->lang = $LANG; + } + + /** + * Set widget skin + * New value must be present in skinAvailable property necessary + * + * @param string $name [optional] + * @return null + */ + public function setSkin($name = '') + { + if (!empty($name) and in_array($name, $this->skinAvailable, true)) { + $this->skinName = $name; + } else { + $this->skinName = $this->config['skinDefault']; + } + } + + /** + * Set new values of properties through the $_GET + * + * @return null + */ + public function setOptions() + { + $this->width -= 2; + if ($this->skipGET === false) { + if (isset($_GET['width']) and (int)$_GET['width'] > 0) { + $this->width = $_GET['width'] - 2; + } + if (isset($_GET['inline']) and (int)$_GET['inline'] > 0) { + $this->inline = $_GET['inline']; + } + if (isset($_GET['view']) and (int)$_GET['view'] > 0) { + $this->view = $_GET['view']; + } + if (isset($_GET['toolbar']) and $_GET['toolbar'] == 'false' or !empty($this->config['HASHTAG'])) { + $this->toolbar = false; + } + if (isset($_GET['adaptive']) and $_GET['adaptive'] == 'true') { + $this->adaptive = true; + } + if (isset($_GET['preview'])) { + $this->preview = $_GET['preview']; + } + if (isset($_GET['lang'])) { + $this->setLang($_GET['lang']); + } + if (isset($_GET['skin'])) { + $this->setSkin($_GET['skin']); + } + } + if ($this->width > 0) { + $this->imgWidth = round(($this->width - (17 + (9 * $this->inline))) / $this->inline); + } + } + + /** + * Let me know if this user was banned + * + * @param int $id + * @return bool + */ + public function isBannedUserId($id) + { + if (!empty($this->data->banned)) { + foreach ($this->data->banned as $key1 => $cacheValue) { + if (!empty($cacheValue->id) and $cacheValue->id === $id) { + if (!empty($this->config['tagsBannedLogins'])) { + foreach ($this->config['tagsBannedLogins'] as $key2 => $configValue) { + if ($configValue['login'] === $cacheValue->login) { + return true; + } + } + } + } + } + } + return false; + } + + /** + * Get number of images without images of banned users + * + * @param object $images + * @return int + */ + public function countAvailableImages($images) + { + $count = 0; + if (!empty($images)) { + foreach ($images as $key => $item) { + if ($this->isBannedUserId($item->authorId) == true) { + continue; + } + $count++; + } + } + return $count; + } +} diff --git a/classes/InWidget/Exception/InWidgetException.php b/classes/InWidget/Exception/InWidgetException.php new file mode 100644 index 0000000..0e38e15 --- /dev/null +++ b/classes/InWidget/Exception/InWidgetException.php @@ -0,0 +1,32 @@ +ERROR #' . $code . ': ' . $text; + if ($code >= 401) { + file_put_contents($cacheFile, $result, LOCK_EX); + } + \Exception::__construct($result, $code); + } +} diff --git a/classes/InWidget/Exception/inWidgetException.php b/classes/InWidget/Exception/inWidgetException.php deleted file mode 100644 index d9e6e9d..0000000 --- a/classes/InWidget/Exception/inWidgetException.php +++ /dev/null @@ -1,32 +0,0 @@ -ERROR #'.$code.': '.$text; - if($code >= 401) { - file_put_contents($cacheFile, $result, LOCK_EX); - } - \Exception::__construct($result, $code); - } -} \ No newline at end of file diff --git a/classes/InstagramScraper.php b/classes/InstagramScraper.php index e3612e1..e16c8bf 100644 --- a/classes/InstagramScraper.php +++ b/classes/InstagramScraper.php @@ -14,4 +14,4 @@ require_once __DIR__ . '/InstagramScraper/Model/Tag.php'; require_once __DIR__ . '/InstagramScraper/Exception/InstagramException.php'; require_once __DIR__ . '/InstagramScraper/Exception/InstagramAuthException.php'; -require_once __DIR__ . '/InstagramScraper/Exception/InstagramNotFoundException.php'; \ No newline at end of file +require_once __DIR__ . '/InstagramScraper/Exception/InstagramNotFoundException.php'; diff --git a/classes/InstagramScraper/Instagram.php b/classes/InstagramScraper/Instagram.php index 6c9c513..efb12ef 100644 --- a/classes/InstagramScraper/Instagram.php +++ b/classes/InstagramScraper/Instagram.php @@ -1487,8 +1487,12 @@ public function login($force = false, $support_two_step_verification = false) ['username' => $this->sessionUsername, 'password' => $this->sessionPassword]); if ($response->code !== 200) { - if ($response->code === 400 && isset($response->body->message) && $response->body->message == 'checkpoint_required' && $support_two_step_verification) { - $response = $this->verifyTwoStep($response, $cookies); + if ($response->code === 400 && isset($response->body->message) && $response->body->message == 'checkpoint_required') { + if($support_two_step_verification) { + $response = $this->verifyTwoStep($response, $cookies); + } else { + throw new InstagramAuthException('Checkpoint required. Disable two step verification or try to login in browser first'); + } } elseif ((is_string($response->code) || is_numeric($response->code)) && is_string($response->body)) { throw new InstagramAuthException('Response code is ' . $response->code . '. Body: ' . $response->body . ' Something went wrong. Please report issue.'); } else { diff --git a/classes/Unirest/Request.php b/classes/Unirest/Request.php index 533546d..68d8b0d 100644 --- a/classes/Unirest/Request.php +++ b/classes/Unirest/Request.php @@ -11,8 +11,8 @@ class Request private static $handle = null; private static $jsonOpts = array(); private static $socketTimeout = null; - private static $verifyPeer = false; // inWidget FIX - private static $verifyHost = false; // inWidget FIX + private static $verifyPeer = false; // inWidget FIX + private static $verifyHost = false; // inWidget FIX private static $auth = array ( 'user' => '', @@ -398,11 +398,11 @@ public static function send($method, $url, $body = null, $headers = array(), $us //echo $url.'
'; if ($method !== Method::GET) { - if ($method === Method::POST) { - curl_setopt(self::$handle, CURLOPT_POST, true); - } else { - curl_setopt(self::$handle, CURLOPT_CUSTOMREQUEST, $method); - } + if ($method === Method::POST) { + curl_setopt(self::$handle, CURLOPT_POST, true); + } else { + curl_setopt(self::$handle, CURLOPT_CUSTOMREQUEST, $method); + } curl_setopt(self::$handle, CURLOPT_POSTFIELDS, $body); } elseif (is_array($body)) { @@ -431,7 +431,7 @@ public static function send($method, $url, $body = null, $headers = array(), $us /* $version = curl_version(); if($version['version'] >= '7.10.8') { - $curl_base_options[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; + $curl_base_options[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; } */ @@ -476,10 +476,11 @@ public static function send($method, $url, $body = null, $headers = array(), $us )); } - //$response = curl_exec(self::$handle); // inWidget FIX - $response = self::curl_redir_exec(self::$handle); // inWidget FIX - $error = curl_error(self::$handle); - $info = self::getInfo(); + // $response = curl_exec(self::$handle); + $response = self::curl_redir_exec(self::$handle); // inWidget FIX + + $error = curl_error(self::$handle); + $info = self::getInfo(); if ($error) { throw new Exception($error); @@ -497,28 +498,33 @@ public static function send($method, $url, $body = null, $headers = array(), $us // inWidget FIX // Function to fix open_basedir restriction with CURLOPT_FOLLOWLOCATION in shared hosting // More: https://stackoverflow.com/questions/2511410/curl-follow-location-error - public static function curl_redir_exec($ch){ - $data = curl_exec($ch); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($http_code == 301 || $http_code == 302) { - $matches = array(); - preg_match('/Location:(.*?)\n/', $data, $matches); - $url = @parse_url(trim(array_pop($matches))); - if (!$url){ - $curl_loops = 0; - return $data; - } - $last_url = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); - if (!$url['scheme']) $url['scheme'] = $last_url['scheme']; - if (!$url['host']) $url['host'] = $last_url['host']; - if (!$url['path']) $url['path'] = $last_url['path']; - $new_url = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query'] ? '?'.$url['query']:''); - curl_setopt($ch, CURLOPT_URL, $new_url); - return self::curl_redir_exec($ch); - } else { - $curl_loops = 0; - return $data; - } + public static function curl_redir_exec($ch) + { + $data = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($http_code == 301 || $http_code == 302) { + $matches = array(); + preg_match('/location:(.*?)\n/i', $data, $matches); + $url = @parse_url(trim(array_pop($matches))); + if (!$url) { + return $data; + } + $last_url = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + if (!$url['scheme']) { + $url['scheme'] = $last_url['scheme']; + } + if (!$url['host']) { + $url['host'] = $last_url['host']; + } + if (!$url['path']) { + $url['path'] = $last_url['path']; + } + $new_url = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query'] ? '?' . $url['query'] : ''); + curl_setopt($ch, CURLOPT_URL, $new_url); + return self::curl_redir_exec($ch); + } else { + return $data; + } } public static function getInfo($opt = false) diff --git a/config.php b/config.php index d365281..d1ade36 100644 --- a/config.php +++ b/config.php @@ -1,124 +1,134 @@ 'fotokto_ru', - - // Primary hashtags - // Separate hashtags by a comma. For example: girl, man - 'HASHTAG' => '', - - // ------------------------------------------------------------- - // Authorization (NOT required) - // ------------------------------------------------------------- - - // Access token granted to your primary account by an Instagram app. - // If you use it, the widget will start sending requests through the official API (https://www.instagram.com/developer/) - 'ACCESS_TOKEN' => '', - - // Login and password of an Instagram account for authorization. - // Authorization is necessary for alternative methods of obtaining data and provides more stability when you using the undocumented API - 'authLogin' => '', - 'authPassword' => '', - - // ------------------------------------------------------------- - // Multi-account configuration - // ------------------------------------------------------------- - - // If you need to separete logins on diffent website pages, just add possible logins to the array below. - // After that you can send login to the widget by GET variable. It workds only with the undocumented API. - // Example: /inwidget/index.php?login=fotokto_ru - 'loginAvailable' => [ - #'fotokto_ru', - #'instagram', - ], - // Same option for tagged media. Add possible tags to the array below. - // Then you can use GET variable for tags. Example: /inwidget/index.php?tag=photography - // You can mix this option with "loginAvailable" and "tagsFromAccountOnly" - 'tagsAvailable' => [ - #'girl', - #'photography', - ], - - // ------------------------------------------------------------- - // Tags - // ------------------------------------------------------------- - - // Specify here list of banned logins. - // Photos of these users will not be display in the widget. - // Separate usernames by a comma. For example: mark18, kitty45 - 'tagsBannedLogins' => '', - - // Search tagged media from your account only [ true / false ] - // To improve search, increase value of the "imgCount" option - 'tagsFromAccountOnly' => false, - - // ------------------------------------------------------------- - // Images - // ------------------------------------------------------------- - - // Random order of pictures [ true / false ] - 'imgRandom' => true, - - // How many pictures the widget will get from Instagram? - 'imgCount' => 30, - - // ------------------------------------------------------------- - // Cache - // ------------------------------------------------------------- - - // Cache expiration time (hours) - 'cacheExpiration' => 6, - - // Skip cache data [ true / false ] - // So mean, requests to Instagram API will be sending every time. - // Warning! Use true value only for debug - 'cacheSkip' => false, - - // Full path to the cache directory - 'cachePath' => __DIR__.'/cache/', - - // ------------------------------------------------------------- - // Skin - // ------------------------------------------------------------- - - // Default skin. - // Possible values: default, modern-blue, modern-green, modern-red, modern-orange, modern-grey, modern-black, modern-violet, modern-yellow - // This option may no effect if you set a skin by $_GET variable - 'skinDefault' => 'default', - - // Possible skin values. - // If you are using a custom skin, add the skin filename in this array without extension. - 'skinAvailable' => ['default', 'modern-blue', 'modern-green', 'modern-red', 'modern-orange', 'modern-grey', 'modern-black', 'modern-violet', 'modern-yellow'], - - // Path to the skins directory - 'skinPath' => 'skins/', - - // ------------------------------------------------------------- - // Lang - // ------------------------------------------------------------- - - // Default language [ ru / en / ua ] or something else from the lang directory. - // This option may no effect if you set a lang by $_GET variable - 'langDefault' => 'ru', - - // Possible language values. - // If you are using another language, add the lang filename in this array without extension. - 'langAvailable' => ['ru','en','ua'], - - // Full path to the langs directory - 'langPath' => __DIR__.'/langs/', - - // Language auto-detection [ true / false ] - // This option may no effect if you set a language by $_GET variable. - 'langAuto' => false, - -); \ No newline at end of file + // ------------------------------------------------------------- + // Main settings + // ------------------------------------------------------------- + + // Primary Instagram login + 'LOGIN' => 'fotokto_ru', + + // Primary hashtags + // Separate hashtags by a comma. For example: girl, man + 'HASHTAG' => '', + + // ------------------------------------------------------------- + // Authorization (NOT required) + // ------------------------------------------------------------- + + // Access token granted to your primary account by an Instagram app. + // If you use it, the widget will start sending requests through the official API (https://www.instagram.com/developer/) + 'ACCESS_TOKEN' => '', + + // Login and password of an Instagram account for authorization. + // Authorization is necessary for alternative methods of obtaining data and provides more stability when you using the undocumented API + 'authLogin' => '', + 'authPassword' => '', + + // ------------------------------------------------------------- + // Multi-account configuration + // ------------------------------------------------------------- + + // If you need to separete logins on diffent website pages, just add possible logins to the array below. + // After that you can send login to the widget by GET variable. It workds only with the undocumented API. + // Example: /inwidget/index.php?login=fotokto_ru + 'loginAvailable' => [ + #'fotokto_ru', + #'instagram', + ], + // Same option for tagged media. Add possible tags to the array below. + // Then you can use GET variable for tags. Example: /inwidget/index.php?tag=photography + // You can mix this option with "loginAvailable" and "tagsFromAccountOnly" + 'tagsAvailable' => [ + #'girl', + #'photography', + ], + + // ------------------------------------------------------------- + // Tags + // ------------------------------------------------------------- + + // Specify here list of banned logins. + // Photos of these users will not be display in the widget. + // Separate usernames by a comma. For example: mark18, kitty45 + 'tagsBannedLogins' => '', + + // Search tagged media from your account only [ true / false ] + // To improve search, increase value of the "imgCount" option + 'tagsFromAccountOnly' => false, + + // ------------------------------------------------------------- + // Images + // ------------------------------------------------------------- + + // Random order of pictures [ true / false ] + 'imgRandom' => true, + + // How many pictures the widget will get from Instagram? + 'imgCount' => 30, + + // ------------------------------------------------------------- + // Cache + // ------------------------------------------------------------- + + // Cache expiration time (hours) + 'cacheExpiration' => 6, + + // Skip cache data [ true / false ] + // So mean, requests to Instagram API will be sending every time. + // Warning! Use true value only for debug + 'cacheSkip' => false, + + // Full path to the cache directory + 'cachePath' => __DIR__ . '/cache/', + + // ------------------------------------------------------------- + // Skin + // ------------------------------------------------------------- + + // Default skin. + // Possible values: default, modern-blue, modern-green, modern-red, modern-orange, modern-grey, modern-black, modern-violet, modern-yellow + // This option may no effect if you set a skin by $_GET variable + 'skinDefault' => 'default', + + // Possible skin values. + // If you are using a custom skin, add the skin filename in this array without extension. + 'skinAvailable' => [ + 'default', + 'modern-blue', + 'modern-green', + 'modern-red', + 'modern-orange', + 'modern-grey', + 'modern-black', + 'modern-violet', + 'modern-yellow' + ], + + // Path to the skins directory + 'skinPath' => 'skins/', + + // ------------------------------------------------------------- + // Lang + // ------------------------------------------------------------- + + // Default language [ ru / en / ua ] or something else from the lang directory. + // This option may no effect if you set a lang by $_GET variable + 'langDefault' => 'ru', + + // Possible language values. + // If you are using another language, add the lang filename in this array without extension. + 'langAvailable' => ['ru', 'en', 'ua'], + + // Full path to the langs directory + 'langPath' => __DIR__ . '/langs/', + + // Language auto-detection [ true / false ] + // This option may no effect if you set a language by $_GET variable. + 'langAuto' => false, + +); diff --git a/index.php b/index.php index b7fa804..e98693d 100644 --- a/index.php +++ b/index.php @@ -1,4 +1,4 @@ -= 5.4.0. Your version: '.phpversion()); -if(!extension_loaded('curl')) die('inWidget required cURL PHP extension. Please, install it or ask your hosting provider.'); - -#ini_set('include_path', __DIR__ .'/' ); +if (phpversion() < "5.4.0") { + die('inWidget required PHP >= 5.4.0. Your version: ' . phpversion()); +} +if (!extension_loaded('curl')) { + die('inWidget required cURL PHP extension. Please, install it or ask your hosting provider.'); +} +#ini_set('include_path', __DIR__ .'/' ); #require_once 'classes/Autoload.php'; + require_once 'classes/InstagramScraper.php'; require_once 'classes/Unirest.php'; require_once 'classes/InWidget.php'; /* ----------------------------------------------------------- - Native initialization + Native initialization ------------------------------------------------------------*/ try { - $inWidget = new \inWidget\Core; - $inWidget->getData(); - include 'template.php'; -} -catch (\Exception $e) { - echo $e->getMessage(); + $inWidget = new \InWidget\Core(); + $inWidget->getData(); + include 'template.php'; +} catch (\Exception $e) { + echo $e->getMessage(); } /* ----------------------------------------------------------- - Custom initialization + Custom initialization ------------------------------------------------------------*/ /* try { - // Options may change through the class constructor. For example: - - $config = array( - 'LOGIN' => 'fotokto_ru', - 'HASHTAG' => '', - 'ACCESS_TOKEN' => '', - 'authLogin' => '', - 'authPassword' => '', - 'tagsBannedLogins' => '', - 'tagsFromAccountOnly' => false, - 'imgRandom' => false, - 'imgCount' => 30, - 'cacheExpiration' => 6, - 'cacheSkip' => false, - 'cachePath' => __DIR__.'/cache/', - 'skinDefault' => 'default', - 'skinPath'=> 'skins/', - 'langDefault' => 'ru', - 'langAuto' => false, - 'langPath' => __DIR__.'/langs/', - ); - - $inWidget = new \inWidget\Core($config); - - // Also, you may change default values of properties - - $inWidget->width = 800; // widget width in pixels - $inWidget->inline = 6; // number of images in single line - $inWidget->view = 18; // number of images in widget - $inWidget->toolbar = false; // show profile avatar, statistic and action button - $inWidget->preview = 'large'; // quality of images: small, large, fullsize - $inWidget->adaptive = false; // enable adaptive mode - $inWidget->skipGET = true; // skip GET variables to avoid name conflicts - $inWidget->setOptions(); // apply new values - - $inWidget->getData(); - include 'template.php'; - - // Also, you may use API methods directly - - // $account = $inWidget->api->getAccountByLogin($config['LOGIN'], $config['ACCESS_TOKEN'], $config['imgCount']); - // $mediasByLogin = $inWidget->api->getMediasByLogin($config['LOGIN'], $config['ACCESS_TOKEN'], $config['imgCount']); - // $mediasByTag = $inWidget->api->getMediasByTag('girl', $config['ACCESS_TOKEN'], $config['imgCount']); + // Options may change through the class constructor. For example: + $config = array( + 'LOGIN' => 'fotokto_ru', + 'HASHTAG' => '', + 'ACCESS_TOKEN' => '', + 'authLogin' => '', + 'authPassword' => '', + 'tagsBannedLogins' => '', + 'tagsFromAccountOnly' => false, + 'imgRandom' => false, + 'imgCount' => 30, + 'cacheExpiration' => 6, + 'cacheSkip' => false, + 'cachePath' => __DIR__.'/cache/', + 'skinDefault' => 'default', + 'skinPath'=> 'skins/', + 'langDefault' => 'ru', + 'langAuto' => false, + 'langPath' => __DIR__.'/langs/', + ); + + $inWidget = new \inWidget\Core($config); + + // Also, you may change default values of properties + + $inWidget->width = 800; // widget width in pixels + $inWidget->inline = 6; // number of images in single line + $inWidget->view = 18; // number of images in widget + $inWidget->toolbar = false; // show profile avatar, statistic and action button + $inWidget->preview = 'large'; // quality of images: small, large, fullsize + $inWidget->adaptive = false; // enable adaptive mode + $inWidget->skipGET = true; // skip GET variables to avoid name conflicts + $inWidget->setOptions(); // apply new values + + $inWidget->getData(); + include 'template.php'; + + // Also, you may use API methods directly + + // $account = $inWidget->api->getAccountByLogin($config['LOGIN'], $config['ACCESS_TOKEN'], $config['imgCount']); + // $mediasByLogin = $inWidget->api->getMediasByLogin($config['LOGIN'], $config['ACCESS_TOKEN'], $config['imgCount']); + // $mediasByTag = $inWidget->api->getMediasByTag('girl', $config['ACCESS_TOKEN'], $config['imgCount']); + +} catch (\Exception $e) { + echo $e->getMessage(); } -catch (\Exception $e) { - echo $e->getMessage(); -} -*/ \ No newline at end of file +*/ diff --git a/langs/en.php b/langs/en.php index 7f66f35..8542cec 100644 --- a/langs/en.php +++ b/langs/en.php @@ -1,4 +1,5 @@ 'We\'re on Instagram:', - 'buttonFollow' => 'View', - 'statPosts' => 'posts', - 'statFollowers' => 'followers', - 'statFollowing' => 'following', - 'imgEmpty' => 'user doesn\'t have any photos yet', - 'imgEmptyByHash' => 'photos by tag #{$hashtag} not found', - 'errorCache' => 'Update cache error. Something went wrong.
Using version from', - 'updateNeeded' => 'Please, update widget to last version from inwidget.ru', -); \ No newline at end of file + 'title' => 'We\'re on Instagram:', + 'buttonFollow' => 'View', + 'statPosts' => 'posts', + 'statFollowers' => 'followers', + 'statFollowing' => 'following', + 'imgEmpty' => 'user doesn\'t have any photos yet', + 'imgEmptyByHash' => 'photos by tag #{$hashtag} not found', + 'errorCache' => 'Update cache error. Something went wrong.
Using version from', + 'updateNeeded' => 'Please, update widget to last version from inwidget.ru', +); diff --git a/langs/ru.php b/langs/ru.php index ab26c21..844e2b4 100644 --- a/langs/ru.php +++ b/langs/ru.php @@ -1,4 +1,5 @@ 'Мы в Instagram:', - 'buttonFollow' => 'Посмотреть', - 'statPosts' => 'посты', - 'statFollowers' => 'подписчики', - 'statFollowing' => 'подписки', - 'imgEmpty' => 'у пользователя нет фотографии', - 'imgEmptyByHash' => 'фотографии по тегу #{$hashtag} не найдены ', - 'errorCache' => 'Ошибка обновления кэша.
Используется версия от', - 'updateNeeded' => 'Обновите виджет до последней версии с сайта inwidget.ru', + 'title' => 'Мы в Instagram:', + 'buttonFollow' => 'Посмотреть', + 'statPosts' => 'посты', + 'statFollowers' => 'подписчики', + 'statFollowing' => 'подписки', + 'imgEmpty' => 'у пользователя нет фотографии', + 'imgEmptyByHash' => 'фотографии по тегу #{$hashtag} не найдены ', + 'errorCache' => 'Ошибка обновления кэша.
Используется версия от', + 'updateNeeded' => 'Обновите виджет до последней версии с сайта inwidget.ru', ); \ No newline at end of file diff --git a/langs/ua.php b/langs/ua.php index b6bea3a..b890f14 100644 --- a/langs/ua.php +++ b/langs/ua.php @@ -1,4 +1,5 @@ 'Ми в Instagram:', - 'buttonFollow' => 'Переглянути', - 'statPosts' => 'пости', - 'statFollowers' => 'підписчики', - 'statFollowing' => 'підписки', - 'imgEmpty' => 'у користувача немає фотографій', - 'imgEmptyByHash' => 'фотографії за тегом #{$hashtag} не знайдені ', - 'errorCache' => 'Помилка оновлення кешу.
Використовується версія від', - 'updateNeeded' => 'Оновлення віджет до останньої версії з сайту inwidget.ru', -); \ No newline at end of file + 'title' => 'Ми в Instagram:', + 'buttonFollow' => 'Переглянути', + 'statPosts' => 'пости', + 'statFollowers' => 'підписчики', + 'statFollowing' => 'підписки', + 'imgEmpty' => 'у користувача немає фотографій', + 'imgEmptyByHash' => 'фотографії за тегом #{$hashtag} не знайдені ', + 'errorCache' => 'Помилка оновлення кешу.
Використовується версія від', + 'updateNeeded' => 'Оновлення віджет до останньої версії з сайту inwidget.ru', +); diff --git a/plugins/adaptive.php b/plugins/adaptive.php index 7da470d..323044b 100644 --- a/plugins/adaptive.php +++ b/plugins/adaptive.php @@ -1,78 +1,82 @@ inline = 6; - $inWidget->view = 12; - } + +function setImageSize($inLine) +{ + echo ' + .widget .data a.image:link, .widget .data a.image:visited { + width: -webkit-calc((100% - (5px + (9px * ' . $inLine . '))) / ' . $inLine . '); + width: -moz-calc((100% - (5px + (9px * ' . $inLine . '))) / ' . $inLine . '); + width: -ms-calc((100% - (5px + (9px * ' . $inLine . '))) / ' . $inLine . '); + width: calc((100% - (5px + (9px * ' . $inLine . '))) / ' . $inLine . '); + } + '; +} + +if (!isset($_GET['inline']) and !isset($_GET['view'])) { + $inWidget->inline = 6; + $inWidget->view = 12; +} + ?> \ No newline at end of file + function setImagesDimensions(){ + var images = $('.widget .data .image'); + images.each(function(val){ + $(this).css('height',$(this).width()); + }); + } + function setParentDimensions(){ + if(window.parent.document){ + var parents = $('iframe[data-inwidget]', window.parent.document); + parents.each(function(val){ + if(window.frameElement){ + if($(this).attr('src') !== $(window.frameElement).attr('src')){ + return; + } + } + $(this).css({ + 'width':'100%', + 'height': $('#widget').outerHeight(true) + }); + }); + } + } + $(document).ready(function(){ + setImagesDimensions(); + setParentDimensions(); + $(window).resize(function(){ + setImagesDimensions(); + setParentDimensions(); + }); + }); + diff --git a/readme_ru.txt b/readme_ru.txt index f576052..f6ff320 100644 --- a/readme_ru.txt +++ b/readme_ru.txt @@ -8,7 +8,7 @@ * * @link https://inwidget.ru * @author Alexandr Kazarmshchikov - * @version 1.3.2 + * @version 1.3.3 * @package inWidget * */ @@ -195,7 +195,7 @@ try { 'langPath' => $_SERVER['DOCUMENT_ROOT'].'/inwidget/langs/', ); - $inWidget = new \inWidget\Core($config); + $inWidget = new \InWidget\Core($config); // Also, you may change default values of properties @@ -252,6 +252,12 @@ catch (\Exception $e) { // История версий: // ---------------------------------------- +inWidget-1.3.3 +Дата: 15 марта 2020 г. + +* Исправлена ошибка парсинга URL при обращении к API, если в HTTP заголовке ответа присутствовал редирект. Не затрагивает версии cURL <= 7.59.0 +* Улучшение совместимости кода с PSR-12 + inWidget-1.3.2 Дата: 10 февраля 2020 г. diff --git a/template.php b/template.php index c34fe37..ff1f151 100644 --- a/template.php +++ b/template.php @@ -1,4 +1,5 @@ - - - - - inWidget - free Instagram widget for your site! - - - - - adaptive === false): ?> - - - + + + + inWidget - free Instagram widget for your site! + + + + + adaptive === false) : ?> + + +
- -
 
-
lang['title']; ?>
-
 
-
- toolbar == true): ?> - - - - - - - - - - -
- - - data->posts; ?> - lang['statPosts'] ?> - - data->followers ?> - lang['statFollowers'] ?> - - data->following ?> - lang['statFollowing'] ?> -
- -
- countAvailableImages($inWidget->data->images); - if($count>0) { - if($inWidget->config['imgRandom'] === true) shuffle($inWidget->data->images); - echo '
'; - foreach ($inWidget->data->images as $key=>$item){ - if($inWidget->isBannedUserId($item->authorId) === true) continue; - switch ($inWidget->preview){ - case 'large': - $thumbnail = $item->large; - break; - case 'fullsize': - $thumbnail = $item->fullsize; - break; - default: - $thumbnail = $item->small; - } - echo ' '; - $i++; - if($i >= $inWidget->view) break; - } - echo '
 
'; - echo '
'; - } - else { - if(!empty($inWidget->config['HASHTAG'])) { - $inWidget->lang['imgEmptyByHash'] = str_replace( - '{$hashtag}', - $inWidget->config['HASHTAG'], - $inWidget->lang['imgEmptyByHash'] - ); - echo '
'.$inWidget->lang['imgEmptyByHash'].'
'; - } - else echo '
'.$inWidget->lang['imgEmpty'].'
'; - } - ?> + +
 
+
lang['title']; ?>
+
 
+
+ toolbar == true) : ?> + + + + + + + + + + +
+ + + data->posts; ?> + lang['statPosts'] ?> + + data->followers ?> + lang['statFollowers'] ?> + + data->following ?> + lang['statFollowing'] ?> +
+ +
+ countAvailableImages($inWidget->data->images); + if ($count > 0) { + if ($inWidget->config['imgRandom'] === true) { + shuffle($inWidget->data->images); + } + echo '
'; + foreach ($inWidget->data->images as $key => $item) { + if ($inWidget->isBannedUserId($item->authorId) === true) { + continue; + } + switch ($inWidget->preview) { + case 'large': + $thumbnail = $item->large; + break; + case 'fullsize': + $thumbnail = $item->fullsize; + break; + default: + $thumbnail = $item->small; + } + echo ' '; + $i++; + if ($i >= $inWidget->view) { + break; + } + } + echo '
 
'; + echo '
'; + } else { + if (!empty($inWidget->config['HASHTAG'])) { + $inWidget->lang['imgEmptyByHash'] = str_replace( + '{$hashtag}', + $inWidget->config['HASHTAG'], + $inWidget->lang['imgEmptyByHash'] + ); + echo '
' . $inWidget->lang['imgEmptyByHash'] . '
'; + } else { + echo '
' . $inWidget->lang['imgEmpty'] . '
'; + } + } + ?>
-data->isBackup)): ?> -
- lang['errorCache'].' '.date('Y-m-d H:i:s',$inWidget->data->lastupdate) .'
'. $inWidget->lang['updateNeeded'] ?> -
- +data->isBackup)) : ?> +
+ lang['errorCache'] . ' ' . date( + 'Y-m-d H:i:s', + $inWidget->data->lastupdate + ) . '
' . $inWidget->lang['updateNeeded'] ?> +
+ \ No newline at end of file + inWidget - free Instagram widget for your site! + https://inwidget.ru + © Alexandr Kazarmshchikov +-->