diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d843b..8e7bd8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -# Instagram Feed Changelog +# Release Notes for Instagram Feed Plugin + +## 2.0.1 - 2022-06-03 [CRITICAL] + +> {note} Instagram has changed the data structure on 06/01/2022. Without this update, the plugin will no longer work. + +### Changed + +- Adaptation to the changes in the data structure of Instagram on 06/01/2022. ## 2.0.0 - 2021-04-29 @@ -26,38 +34,45 @@ ## 1.1.7 - 2021-07-19 ### Changed + - Add PHP 8 to composer version constraint. ## 1.1.6 - 2021-07-16 ### Changed + - Removed file_get_contents() and always use Guzzle for all requests. - Changed link to new documentation. ## 1.1.5 - 2021-06-16 ### Fixed + - If you use a volume to store the images, the first time displaying the images failed. - In a scenario, where the image files already exists on the volume but without an asset entry in Craft, the files on the volume will be deleted and downloaded again. ## 1.1.4 - 2021-06-11 ### Fixed + - In some cases, you get the old tag structure. So we accept both now. ## 1.1.3 - 2021-06-09 ### Fixed + - Instagram has changed the structure of the tag data. We have updated the code accordingly. ## 1.1.2 - 2021-05-12 ### Changed + - We made the plugin compatible to older Craft versions again. Changed the required version back to >= 3.0.0. ## 1.1.1 - 2021-04-29 ### Fixed + - We now require Craft >= 3.5.0. ## 1.1.0 - 2021-04-29 @@ -65,29 +80,37 @@ > {note} Since the end of April 2021, Instagram sets the "cross-origin-resource-policy" header to "same-origin" to all their images, which means that your browser is not allowed to load the images inside another website which is not "instagram.com". Starting with this release of the plugin we download, store and serve the images locally. This may have an impact on your website, and you should read the sections "[Local storage](https://github.com/codemonauts/craft-instagram-feed#local-storage)" and "[Blocked requests](https://github.com/codemonauts/craft-instagram-feed#blocked-requests)" carefully. ### Added + - Downloading and storing the Instagram images locally to either Craft's storage path or a volume and path you can configure. -- New keys `imageSource` and `thumbnailSource` in the array of posts, that hold the original image URLs from Instagram. +- New keys `imageSource` and `thumbnailSource` in the array of posts, that hold the original image URLs from Instagram. - New key `asset` in the array of posts, that is an asset element of the Instagram image stored on a volume (only available when using a volume to store the image). ### Changed + - The keys `src`, `thumbnail` and `image` in the array of posts now have URLs pointing to your local copy of the images, which the plugin has downloaded and stored on your server. ## 1.0.7 - 2021-02-26 + ### Fixed -- Fixed videos without information about having an audio. + +- Fixed videos without information about having an audio. ## 1.0.6 - 2021-02-16 + ### Added + - Enable environment variables for setting username (thanks to @niektenhoopen) - Add rel noopener and noreferrer properties to links in the README (thanks to @JayBox325) - Add full image URL attribute. - Add hasAudio attribute. ### Changed + - Prevent request storm if no items are cached and the request failed. - Set default timeout for requests from 5 to 10 seconds. ### Fixed + - Fixed background color of icon - Add trailing slash to all requests - Prevent empty user agent strings @@ -95,24 +118,32 @@ - Catch all exceptions from Guzzle requests ## 1.0.5 - 2020-04-15 + ### Added + - Switch to write a dump file of the response from Instagram -- Switch to use a proxy (beta) +- Switch to use a proxy (beta) ### Changed + - Improve documentation ### Fixed + - HTTP headers send with file_get_contents ## 1.0.4 - 2019-09-15 + ### Fixed + - Settings in CP could not be saved due to a validation rule. - Handle Instagram error response and return empty feed. - Fixed timeout for file stream (float not microseconds). ## 1.0.3 - 2019-09-14 + ### Added + - Timestamps of the photos (thanks to @devotoare) - Timeout fetching the Instgram page, default to 5 seconds, can be changed in the config file (thanks to @mhayes14) - Support for hashtags (thanks to @JeroenOnstuimig) @@ -121,20 +152,28 @@ - Debugging Output (log level debug) ### Fixed + - Some typos (thanks to @ryanpcmcquen) ## 1.0.2 - 2019-05-20 + ### Added + - Captions of posts ## 1.0.1 - 2019-04-14 + ### Added + - Overwrite account name on function call - Add some more logging ### Changed + - Cache account data by account name ## 1.0.0 - 2019-04-06 + ### Added + - Initial release diff --git a/README.md b/README.md index c02d02f..7c5100c 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A plugin for Craft CMS that helps you get your Instagram feed data. If you want to add your (or someone else) Instagram feed on your site, you can use this plugin to fetch and cache the feed. It returns the image source, the number of likes and comments and the shortcode of the posts. -This only works with **public** profiles. +This only works with **public** profiles. ## Requirements - * Craft CMS >= 4.0.0 +* Craft CMS >= 4.0.0 ## Installation @@ -21,12 +21,10 @@ Open your terminal and go to your Craft project: ``` shell cd /path/to/project composer require codemonauts/craft-instagram-feed -./craft install/plugin instagramfeed +./craft plugin/install instagramfeed ``` -Switch to the settings page in the control panel and enter the name of the Instagram account you want to fetch. - -You can also configure a volume where the images are stored locally. +You can also install the plugin via the Plugin Store in the Craft Control Panel. ## Documentation @@ -34,14 +32,15 @@ You find the plugin documentation [here](https://plugins.codemonauts.com/plugins ## Blocked requests -As you know, Instagram is a Walled Garden and they are not very happy to see *their* data on other sites. And they are heavily working on blocking requests not coming from their platforms. So something can break in this plugin at anytime, when trying to fetch the feed. +As you know, Instagram is a Walled Garden, and they are not very happy to see *their* data on other sites. And they are heavily working on blocking requests not coming from their platforms. So something can break in this plugin at anytime, when trying to fetch the feed. Known actions from Instagram: - * 2018, disabling the old, public API. - * March 2020, disabling the token authentication for their new API. - * April 2020, blocking IP addresses from IP ranges not used for client access. - * April 2021, using "cross-origin-resource-policy" with "same-site" to block browsers from loading images inside another website which is not "instagram.com". +* 2018, disabling the old, public API. +* March 2020, disabling the token authentication for their new API. +* April 2020, blocking IP addresses from IP ranges not used for client access. +* April 2021, using "cross-origin-resource-policy" with "same-site" to block browsers from loading images inside another website which is not "instagram.com". +* June 2022, relaunch of the Instagram website in React. The JSON is now fetched in a second request. Please be aware, that you use this plugin at your own risk. Please use this plugin only in a fair manner, for example to show the images of your own Instagram account on your website and link them back to the original post on Instagram. This symbiosis should be fine for Instagram. diff --git a/composer.json b/composer.json index 56f2ac1..49a7e83 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "codemonauts/craft-instagram-feed", "description": "Craft CMS plugin to receive Instagram feed data as variable in templates.", - "version": "2.0.0", + "version": "2.0.1", "type": "craft-plugin", "keywords": [ "craft", @@ -23,9 +23,8 @@ "issues": "https://github.com/codemonauts/craft-instagram-feed/issues" }, "require": { - "craftcms/cms": "^4.0.0-alpha.1", - "ext-json": "*", - "php": "^8.0" + "craftcms/cms": "^4.0.0", + "ext-json": "*" }, "autoload": { "psr-4": { diff --git a/src/InstagramFeed.php b/src/InstagramFeed.php index cd95d59..9fd7b0d 100644 --- a/src/InstagramFeed.php +++ b/src/InstagramFeed.php @@ -72,6 +72,11 @@ public function init() ]; }); } + + // Prefetch account after save settings + Event::on(Plugin::class, Plugin::EVENT_AFTER_SAVE_SETTINGS, function () { + InstagramFeed::getInstance()->instagramService->getFeed(); + }); } /** @@ -120,4 +125,15 @@ protected function settingsHtml(): ?string ] ); } + + /** + * Returns the Instagram service component. + * + * @return InstagramService + * @throws \yii\base\InvalidConfigException + */ + public function getInstagramService(): InstagramService + { + return $this->get('instagramService'); + } } diff --git a/src/services/InstagramService.php b/src/services/InstagramService.php index fe759cc..98624ed 100644 --- a/src/services/InstagramService.php +++ b/src/services/InstagramService.php @@ -91,10 +91,11 @@ public function getFeed(string $accountOrTag = null): array } if (empty($cachedItems)) { - // If the cache is empty (e.g. first request ever) and the request fails, we are stopping requests for 15 minutes. - Craft::info('Cache is empty and no items could be fetched. Stopping requests for 15 minutes.', 'instagramfeed'); + // If the cache is empty (e.g. first request ever) and the request fails, we are stopping requests for some time. + $waitTime = $this->canUseProxy() ? 10 : 900; + Craft::info('Cache is empty and no items could be fetched. Stopping requests for ' . $waitTime . ' seconds.', 'instagramfeed'); $cacheService->set('instagram_data_' . $hash, [], 2592000, $dependency); - $cacheService->set('instagram_update_error_' . $hash, true, 900, $dependency); + $cacheService->set('instagram_update_error_' . $hash, true, $waitTime, $dependency); return []; } @@ -120,11 +121,20 @@ private function getInstagramAccountData(string $account): array $html = $this->fetchInstagramPage($account . '/'); if (null === $html) { - Craft::error('Instagram profile data could not be fetched. Wrong account name or not a public profile.', 'instagramfeed'); + Craft::error('Instagram profile data could not be fetched.', 'instagramfeed'); return []; } + if ($this->canUseProxy()) { + $obj = $this->parseProxyResponse($html); + if (false === $obj) { + return []; + } + + return $this->flattenMediaArray($obj['data']['user']['edge_owner_to_timeline_media']['edges'], self::STRUCTURE_VERSION_1); + } + $obj = $this->parseInstagramResponse($html); if (empty($obj)) { return []; @@ -165,6 +175,15 @@ private function getInstagramTagData(string $tag): array return []; } + if ($this->canUseProxy()) { + $obj = $this->parseProxyResponse($html); + if (false === $obj) { + return []; + } + + return $this->flattenMediaArray($obj['data']['recent']['sections'], self::STRUCTURE_VERSION_2); + } + $obj = $this->parseInstagramResponse($html); if (empty($obj)) { return []; @@ -262,6 +281,18 @@ private function fetchInstagramPage(string $path): ?string return (string)$response->getBody(); } + /** + * Function to parse the response body from the proxy. + * + * @param string $response + * + * @return mixed + */ + private function parseProxyResponse(string $response) + { + return json_decode($response, true); + } + /** * Function to parse the response body from Instagram * @@ -500,4 +531,14 @@ private function populateImages(array $items): array return $items; } + + /** + * Whether the plugin can use the proxy. + * + * @return bool + */ + public function canUseProxy(): bool + { + return (InstagramFeed::$settings->useProxy && InstagramFeed::$settings->proxyKey !== ''); + } }