From 92f9c51ed6b51d6d5a12d950ac244219439e3d89 Mon Sep 17 00:00:00 2001 From: Thomas Schulze Date: Sat, 6 Apr 2019 12:30:41 +0200 Subject: [PATCH] prepare for plugin store --- CHANGELOG.md | 5 ++ README.md | 45 +++++++++++--- composer.json | 77 ++++++++++++------------ resources/instagram.png | Bin 0 -> 6247 bytes src/InstagramFeed.php | 71 ++++++---------------- src/models/Settings.php | 8 ++- src/services/InstagramService.php | 42 ++++++++----- src/templates/settings.twig | 5 +- src/translations/en/instagramfeed.php | 8 --- src/variables/InstagramFeedVariable.php | 4 +- 10 files changed, 138 insertions(+), 127 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 resources/instagram.png delete mode 100644 src/translations/en/instagramfeed.php diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..50bb7f0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Instagram Feed Changelog + +## 1.0.0 - 2019-04-06 +### Added +- Initial release diff --git a/README.md b/README.md index f157335..6744b96 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,51 @@ # Instagram feed plugin for Craft CMS 3.x -A really basic Twig extension for CraftCMS (Craft3.x) that helps you get your instagram feed data +![Icon](resources/instagram.png) +A plugin for Craft CMS that helps you get your Instagram feed data. + +## Background + +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. ## Requirements -This plugin requires Craft CMS 3.0.0 or later. + * Craft CMS >= 3.0.0 ## Installation -To install the plugin, follow these instructions. +Open your terminal and go to your Craft project: + +``` shell +cd /path/to/project +composer require codemonauts/codemonauts/craft-instagram-feed +``` + +In the control panel, go to Settings → Plugins and click the “install” button for *Instagram feed*. Then you will be redirected to the settings page, where you can enter the username of the Instagram account you want to fetch. + +## Usage -1. Open your terminal and go to your Craft project: +To fetch the feed in your template, just iterate like this: - cd /path/to/project +``` twig +{% for item in craft.instagram.getFeed() %} + + + +

{{ item.likes }} Likes / {{ item.comments }} Comments

+{% endfor %} +``` -2. Then tell composer to load the plugin: +In PHP do: - composer require https://github.com/codemonauts/craft-instagram-feed +``` php +$feed = InstagramFeed::getInstance()->instagramService->getFeed(); +``` -3. In the control panel, go to Settings → Plugins and click the “install” button for Instragram Feed. +## Caching - +The feed will be cached for 1 month but will be checked and updated evey 6 hours. If the update fails, the cached feed is used and the update stops for 15 minutes before checking again. -Brought to you by [Codemonauts](https://codemonauts.com) +With ❤ by [codemonauts](https://codemonauts.com) diff --git a/composer.json b/composer.json index cfd6a83..7d3f9a2 100644 --- a/composer.json +++ b/composer.json @@ -1,39 +1,42 @@ { - "name": "codemonauts/craft-instagram-feed", - "description": "Craft 3 plugin to receive instragram feed data as variable in templates.", - "version": "1.0.0", - "type": "craft-plugin", - "keywords": [ - "craft", - "cms", - "craftcms", - "craft-plugin" - ], - "autoload": { - "psr-4": { - "codemonauts\\instagramfeed\\": "src/" - } - }, - "authors": [ - { - "name": "Codemonauts", - "homepage": "https://www.codemonauts.com" - } - ], - "require": { - "craftcms/cms": "^3.0.0" - }, - "support": { - "docs": "https://github.com/codemonauts/craft-instagram-feed/blob/master/README.md", - "issues": "https://github.com/codemonauts/craft-instagram-feed/issues" - }, - "extra": { - "handle": "instagramfeed", - "name": "Instagram Feed", - "developer": "codemonauts", - "developerUrl": "https://codemonauts.com", - "hasCpSection": false, - "hasSettings": false, - "class": "codemonauts\\instagramfeed\\InstagramFeed" + "name": "codemonauts/craft-instagram-feed", + "description": "Craft CMS plugin to receive Instragram feed data as variable in templates.", + "version": "1.0.0", + "type": "craft-plugin", + "keywords": [ + "craft", + "cms", + "craftcms", + "craft-plugin", + "instagram" + ], + "license": "MIT", + "authors": [ + { + "name": "codemonauts", + "homepage": "https://codemonauts.com" } - } \ No newline at end of file + ], + "support": { + "source": "https://github.com/codemonauts/craft-instagram-feed", + "docs": "https://github.com/codemonauts/craft-instagram-feed/blob/master/README.md", + "issues": "https://github.com/codemonauts/craft-instagram-feed/issues" + }, + "require": { + "craftcms/cms": "^3.0.0", + "ext-json": "*" + }, + "autoload": { + "psr-4": { + "codemonauts\\instagramfeed\\": "src/" + } + }, + "extra": { + "handle": "instagramfeed", + "class": "codemonauts\\instagramfeed\\InstagramFeed", + "name": "Instagram Feed", + "description": "Receive Instagram feed data as variable in templates.", + "hasCpSection": false, + "hasSettings": false + } +} diff --git a/resources/instagram.png b/resources/instagram.png new file mode 100644 index 0000000000000000000000000000000000000000..1074f209d8b469ce97184aaa5210bde5baf90cfd GIT binary patch literal 6247 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 RV^#nF002ovPDHLkV1oR8+(`fc literal 0 HcmV?d00001 diff --git a/src/InstagramFeed.php b/src/InstagramFeed.php index 232ab1c..a567208 100644 --- a/src/InstagramFeed.php +++ b/src/InstagramFeed.php @@ -1,100 +1,69 @@ setComponents([ 'instagramService' => InstagramService::class, ]); - Event::on( - Plugins::class, - Plugins::EVENT_AFTER_INSTALL_PLUGIN, - function(PluginEvent $event) { - if ($event->plugin === $this) { - } - } - ); - Event::on(CraftVariable::class, CraftVariable::EVENT_INIT, function(Event $event) { $variable = $event->sender; $variable->set('instagram', InstagramFeedVariable::class); }); - - Craft::info( - Craft::t( - 'instagramfeed', - '{name} plugin loaded', - ['name' => $this->name] - ), - __METHOD__ - ); } - public function afterInstall () + /** + * @inheritDoc + */ + public function afterInstall() { parent::afterInstall(); - if (Craft::$app->getRequest()->getIsConsoleRequest()) + if (Craft::$app->getRequest()->getIsConsoleRequest()) { return; + } Craft::$app->getResponse()->redirect( UrlHelper::cpUrl('settings/plugins/instagramfeed') )->send(); } - // Protected Methods - // ========================================================================= - + /** + * @inheritDoc + */ protected function createSettingsModel() { - return new \codemonauts\instagramfeed\models\Settings(); + return new Settings(); } + /** + * @inheritDoc + */ protected function settingsHtml() { - return Craft::$app->getView()->renderTemplate( - 'instagramfeed/settings', - ['settings' => $this->getSettings()] + return Craft::$app->getView()->renderTemplate('instagramfeed/settings', [ + 'settings' => $this->getSettings() + ] ); } } diff --git a/src/models/Settings.php b/src/models/Settings.php index 71dcda6..f033fcd 100644 --- a/src/models/Settings.php +++ b/src/models/Settings.php @@ -6,12 +6,18 @@ class Settings extends Model { + /** + * @var string The Instagram account to get the feed from + */ public $instagramUser = ''; + /** + * @inheritdoc + */ public function rules() { return [ ['instagramUser', 'required'], ]; } -} \ No newline at end of file +} diff --git a/src/services/InstagramService.php b/src/services/InstagramService.php index 2537a68..41bdca5 100644 --- a/src/services/InstagramService.php +++ b/src/services/InstagramService.php @@ -9,42 +9,51 @@ class InstagramService extends Component { - public function getUserData() + /** + * Returns the current Instagram feed of the configured account. + * + * @return array The feed data + */ + public function getFeed() { $uptodate = Craft::$app->getCache()->get('instagram_uptodate'); $cachedItems = Craft::$app->getCache()->get('instagram_data'); $timeout = Craft::$app->getCache()->get('instagram_update_error'); if ($uptodate === false && $timeout === false) { - $items = $this->getInstagramData(); if (!empty($items)) { Craft::$app->getCache()->set('instagram_data', $items, 2592000); Craft::$app->getCache()->set('instagram_uptodate', true, 21600); + return $items; } - if(!empty($cachedItems)){ - //If not updated expand cache time and set update to 15min to stop from retrying every request + if (!empty($cachedItems)) { + // If not updated expand cache time and set update to 15min to stop from retrying every request Craft::$app->getCache()->set('instagram_data', $cachedItems, 2592000); Craft::$app->getCache()->set('instagram_update_error', true, 900); } - - // Future: Notify admin if empty items and cached instagram cache is too old } + return $cachedItems; } + /** + * Fetches the feed from the public Instagram profile page. + * + * @return array|bool + */ private function getInstagramData() { $items = []; $user = InstagramFeed::getInstance()->getSettings()->instagramUser; - if(empty($user)){ + if (empty($user)) { return false; } $url = sprintf("https://www.instagram.com/%s", $user); - $html = file_get_contents($url); + $html = @file_get_contents($url); if ($html !== false) { @@ -52,28 +61,31 @@ private function getInstagramData() $arr = explode(';', $arr[1]); $obj = json_decode($arr[0], true); - if(!array_key_exists('ProfilePage', $obj['entry_data'] )){ + if (!array_key_exists('ProfilePage', $obj['entry_data'])) { return false; } + $mediaArray = $obj['entry_data']['ProfilePage'][0]['graphql']['user']['edge_owner_to_timeline_media']['edges']; foreach ($mediaArray as $media) { - $item['src'] = $this->getBestPicture($media['node']['thumbnail_resources']); - - // Future: Option to Save File to S3 Check Media if not exists save media and if exists and saved add to array - $item['likes'] = $media['node']['edge_liked_by']['count']; $item['comments'] = $media['node']['edge_media_to_comment']['count']; $item['shortcode'] = $media['node']['shortcode']; - $items[] = $item; } + return $items; } + return false; } + /** + * @param array $pictures The array of pictures to chose the best version from. + * + * @return string The URL of the best picture. + */ private function getBestPicture($pictures) { $url = ''; @@ -82,7 +94,6 @@ private function getBestPicture($pictures) if (is_array($pictures)) { foreach ($pictures as $picture) { $pixels = $picture['config_width'] * $picture['config_height']; - if ($pixels > $maxPixels) { $url = $picture['src']; @@ -90,6 +101,7 @@ private function getBestPicture($pictures) } } } + return $url; } } diff --git a/src/templates/settings.twig b/src/templates/settings.twig index dfceab8..0ab1485 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -1,14 +1,13 @@ {% import "_includes/forms" as forms %} - {{ forms.textField({ label: "Username"|t("instagramfeed"), id: 'instagramUser', name: 'instagramUser', - instructions: "Please, enter your Instagram Username."|t("instagramfeed"), + instructions: "Please, enter your Instagram username."|t("instagramfeed"), value: settings.instagramUser, type: 'text', required: true }) }} -

{{ "Make sure the entered instagram user profil is not private."|t("instagramfeed") }}

\ No newline at end of file +

{{ "Make sure the entered instagram user profil is not private."|t("instagramfeed") }}

diff --git a/src/translations/en/instagramfeed.php b/src/translations/en/instagramfeed.php deleted file mode 100644 index 68441e4..0000000 --- a/src/translations/en/instagramfeed.php +++ /dev/null @@ -1,8 +0,0 @@ - 'Username', - 'Please, enter your Instagram Username.' => 'Please, enter your instagram username.', - 'Make sure the entered instagram user profil is not private.' => 'Make sure your the entered instagram user profil is not private.', -]; \ No newline at end of file diff --git a/src/variables/InstagramFeedVariable.php b/src/variables/InstagramFeedVariable.php index e155b6a..5d73010 100644 --- a/src/variables/InstagramFeedVariable.php +++ b/src/variables/InstagramFeedVariable.php @@ -6,9 +6,9 @@ class InstagramFeedVariable { - public function getUserData() + public function getFeed() { - return InstagramFeed::getInstance()->instagramService->getUserData(); + return InstagramFeed::getInstance()->instagramService->getFeed(); } }