Skip to content

Instantly share code, notes, and snippets.

@dfelton
Last active March 4, 2019 20:54
Show Gist options
  • Save dfelton/2216ccff546bf94d6427418888954a1b to your computer and use it in GitHub Desktop.
Save dfelton/2216ccff546bf94d6427418888954a1b to your computer and use it in GitHub Desktop.
Instagram Feed for Magento
Instructions on how to add an Instagram Feed to a Magento Site.
Includes:
Block Class w/ base template for rendering the feed
System -> Configuration options for connecting to the client's Instagram account
Default CSS Styles
<!--
Adds the necessary System -> Configuration fields to store the client's
account information and configuration settings for the feed.
-->
<config>
<tabs>
<firstscribe translate="label" module="firstscribe_grizzly">
<label>Client Name</label>
<sort_order>20</sort_order>
</firstscribe>
</tabs>
<sections>
<firstscribe_client translate="label" module="firstscribe_grizzly">
<class>separator-top</class>
<label>Client Name</label>
<tab>firstscribe</tab>
<frontend_type>text</frontend_type>
<sort_order>100</sort_order>
<show_in_default>1</show_in_default>
<groups>
<instagram translate="label" module="firstscribe_grizzly">
<label>Instagram Account</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<fields>
<user_id>
<label>User Id</label>
<comment>This is different from your login username.</comment>
<frontend_type>text</frontend_type>
<show_in_default>1</show_in_default>
<sort_order>0</sort_order>
<frontend_class>validate-digits</frontend_class>
</user_id>
<access_token translate="label">
<label>Access Token</label>
<frontend_type>obscure</frontend_type>
<backend_model>adminhtml/system_config_backend_encrypted</backend_model>
<comment><![CDATA[<a href="https://www.instagram.com/developer/authentication/" target="_blank">Read Documentation</a>]]></comment>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
</access_token>
<photo_count>
<label>Photo count</label>
<comment>Number of photos to display in the feed.</comment>
<frontend_type>text</frontend_type>
<show_in_default>1</show_in_default>
<frontend_class>validate-digits required-entry validate-greater-than-zero</frontend_class>
<sort_order>0</sort_order>
</photo_count>
</fields>
</instagram>
</groups>
</firstscribe_client>
</sections>
</config>
<?php
/**
* Instagram Helper
*
* @category FirstScribe
* @package FirstScribe_Client
* @author Darren D Felton <dfelton@firstscribe.com>
*/
class FirstScribe_Client_Helper_Instagram extends Mage_Core_Helper_Abstract
{
const XML_PATH_ACCESS_TOKEN = 'firstscribe_client/instagram/access_token';
const XML_PATH_USER_ID = 'firstscribe_client/instagram/user_id';
const XML_PATH_PHOTO_COUNT = 'firstscribe_client/instagram/photo_count';
/**
* Return the user id for the client's Instagram account
* @return string
*/
public function getUserId()
{
return Mage::getStoreConfig(self::XML_PATH_USER_ID);
}
/**
* Return the access token for the client's Instagram account
*
* @return string
*/
public function getAccessToken()
{
/* @var $core Mage_Core_Helper_Data */
$core = Mage::helper('core');
return $core->decrypt(Mage::getStoreConfig(self::XML_PATH_ACCESS_TOKEN));
}
/**
* Return the number of items to fetch for the feed.
*
* @return string
*/
public function getPhotoCount()
{
return Mage::getStoreConfig(self::XML_PATH_PHOTO_COUNT);
}
}
<?php
/**
* Instagram Feed
*
* @category FirstScribe
* @package FirstScribe_Grizzly
* @author Darren D Felton <dfelton@firstscribe.com>
*/
/* @var $this FirstScribe_Grizzly_Block_Instagram_Feed */
/* @var $item Varien_Object */
$j = count($this->getFeed());
$i = 0;
?>
<div class="instagram-feed-container" id="list_instagram">
<div class="instagram-feed">
<?php foreach ($this->getFeed() as $item): ?>
<a href="#instagram_feed_<?php echo $item->getId() ?>" data-fancybox-group="instagram" class="<?php echo $this->getListClass(++$i, $j, $item, 'item', 6, 'fancybox') ?>">
<img src="<?php echo $item->getData('image_preview') ?>" alt="<?php echo $this->quoteEscape($item->getData('caption')) ?>" />
<div class="absolute-cover inside">
<div class="valign-wrap">
<div class="valign-middle">
<div class="animated mglass-white"></div>
</div>
</div>
</div>
</a>
<div class="overlay-container hidden">
<div class="instagram-popup" id="instagram_feed_<?php echo $item->getId() ?>">
<img src="<?php echo $item->getData('image_full') ?>" alt="<?php echo $this->quoteEscape($item->getData('caption')) ?>" />
<div class="std">
<p><?php echo $item->getData('caption') ?></p>
<p class="a-center">
<a href="<?php echo $item->getData('link') ?>" target="_blank" class="button view">
<?php echo $this->__('View on Instagram') ?></a>
</div>
</div>
</div>
<?php endforeach ?>
</div>
</div>
/* Animations */
@keyframes bounceInDown{60%,75%,90%,from,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}
.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}
/* Global: Helpers */
.absolute-cover{bottom:0;left:0;padding:15px;position:absolute;right:0;top:0}
.valign-wrap{display:table;height:100%;width:100%}
.valign-middle{display:table-cell;vertical-align:middle}
/* Instagram Feed */
.instagram-feed{border:5px solid #fff;margin:0 auto;max-width:100%;width:1920px}
.instagram-feed .item{border:5px solid #fff;display:block;float:left;overflow:hidden;position:relative;width:16%;width:calc(100%/6)}
.instagram-feed .item:hover{cursor:pointer}
.instagram-feed .item:hover .inside{background:#a60918;opacity:.6}
.instagram-feed .item:hover .mglass-white{animation-name:bounceInDown;opacity:1}
.instagram-feed img{max-width:100%}
.instagram-feed .mglass-white{opacity:0;height:30px;margin:0 auto;position:relative;transition:opacity 1000ms;width:30px}
.instagram-feed .inside{transition:background-color 500ms,opacity 500ms}
.instagram-popup{border-bottom-left-radius:10px;border-bottom-right-radius:10px;max-width:640px;overflow:hidden;width:100%}
.instagram-popup img{max-width:100%}
.instagram-popup .std{border-top:5px solid #a60918;padding:10px}
@media(max-width:1600px){
.instagram-feed .item{width:20%}}
@media(max-width:1280px){
.instagram-feed .item{width:25%}}
@media(max-width:960px){
.instagram-feed .item{width:33%;width:calc(100%/3)}}
@media(max-width:640px){
.instagram-feed .item{width:50%}}
@media(max-width:500px){
.instagram-feed .item{float:none;margin:0 auto;width:320px}}
<?php
/**
* Provides helper methods for HTML lists.
*
* @category FirstScribe
* @package FirstScribe_Trait
* @author Darren D Felton <dfelton@firstscribe.com>
*/
trait FirstScribe_Trait_Utilities_Html_List
{
/**
* Return a string of CSS class names
*
* @param int $i REQUIRED; Current iteration
* @param int $j REQUIRED; Total iterations
* @param object|int|string $item OPTIONAL; Object model or id of the item
* @param string $name OPTIONAL; CSS class name to use for items
* @param int $cols OPTIONAL; Max column CSS class names
* @param array|string $additional OPTIONAL; Additional CSS class names desired for every item
* @return string
*/
public function getListClass($i, $j, $item = null, $name = 'item', $cols = 0, $additional = array())
{
$class = array($name, $name.'-'.$i);
if (is_object($item) && method_exists($item, 'getId')) {
$class[] = $name.'-id-'.$item->getId();
} elseif (is_string($item) || is_int($item)) {
$class[] = $name.'-id-'.$item;
}
$class[] = $i% 2 ? 'odd' : 'even';
for ($k=3; $k <= $cols; $k++) {
if (0 == ($i-1) % $k || 1 == $i) {
$class[] = 'col'.$k.'-start';
} elseif (0 == $i % $k) {
$class[] = 'col'.$k.'-end';
}
}
if (1 == $i) $class[] = 'first';
if ($j == $i) $class[] = 'last';
if (is_string($additional)) {
$additional = array($additional);
}
$class = array_merge($class, $additional);
return implode(' ', $class);
}
}
<?php
/**
* Instagram Feed
*
* @category FirstScribe
* @package FirstScribe_Client
* @author Darren D Felton <dfelton@firstscribe.com>
*/
class FirstScribe_Client_Block_Instagram_Feed extends Mage_Core_Block_Template
{
use FirstScribe_Trait_Utilities_Html_List;
// Response codes from Instagram
const META_CODE_INVALID_ACCESS_TOKEN = 400;
const META_CODE_OK = 200;
/**
* @var array
*/
protected $_feed;
/**
* @var FirstScribe_Client_Helper_Instagram
*/
protected $_helper;
public function getFeed()
{
if (is_null($this->_feed)) {
$this->initFeed();
}
return $this->_feed;
}
/**
* Initialize the instagram feed.
*/
protected function initFeed()
{
$json = file_get_contents($this->getFeedUrl());
if (false === $json) {
$this->logException('Failed to load feed contents.');
$this->_feed = array();
return;
}
$object = json_decode($json);
switch ($object->meta->code) {
case self::META_CODE_OK:
$this->_feed = $this->parseFeed($object->data);
break;
case self::META_CODE_INVALID_ACCESS_TOKEN:
$this->logException('Invalid Instagram access_token.');
$this->_feed = array();
break;
default:
$this->logException('Unknown response meta code:'.$object->meta->code);
$this->_feed = array();
break;
}
return $this;
}
/**
* Parse the Instagram feed into a friendly array
*
* @param array $items
* @return array
*/
protected function parseFeed(array $items)
{
/* @var $item stdClass */
$feed = array();
foreach ($items as $item) {
// Only images are handled at this point.
if ($item->type != 'image') {
continue;
}
$data = array(
'id' => $item->id,
'image_preview' => $item->images->low_resolution->url,
'image_full' => $item->images->standard_resolution->url,
'link' => $item->link,
'caption' => $item->caption->text
);
$feed[] = new Varien_Object($data);
}
return $feed;
}
/**
* Log an exception message
*
* @param string $message
* @return FirstScribe_Client_Block_Instagram_Feed
*/
protected function logException($message)
{
try {
Mage::throwException('Invalid Instagram access_token.');
} catch (Exception $e) {
Mage::logException($e);
}
return $this;
}
/**
* Return the URL for the Instagram feed, including all parameters for it.
*
* @return string
*/
public function getFeedUrl()
{
return 'https://api.instagram.com/v1/users/'.$this->getUserId()
.'/media/recent/?access_token='.$this->getAccessToken()
.'&count='.$this->getPhotoCount();
}
/**
* Return the client's access token.
*
* @return string
*/
public function getAccessToken()
{
return $this->getInstagramHelper()->getAccessToken();
}
/**
* Return the client's user id.
*
* @return string
*/
public function getUserId()
{
return $this->getInstagramHelper()->getUserId();
}
/**
* Return the photo count for the feed.
*
* @return string
*/
public function getPhotoCount()
{
return $this->getInstagramHelper()->getPhotoCount();
}
/**
* @return FirstScribe_Client_Helper_Instagram
*/
public function getInstagramHelper()
{
if (!$this->_helper) {
$this->_helper = $this->helper('firstscribe_client/instagram');
}
return $this->_helper;
}
protected function _prepareLayout()
{
parent::_prepareLayout();
$this->setData('cache_lifetime', '21600'); // 6 hours
return $this;
}
protected function _toHtml()
{
return $this->getFeed() ? parent::_toHtml() : '';
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment