<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Model;

use Fastly\Cdn\Helper\Data;
use Magento\Directory\Api\CountryInformationAcquirerInterface;
use Magento\Directory\Model\CountryFactory;
use Magento\Directory\Model\RegionFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\HTTP\Adapter\CurlFactory;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;
use Magento\Framework\Stdlib\DateTime\DateTime;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Request\Http;

/**
 * Class Statistic
 *
 * @package Fastly\Cdn\Model
 */
class Statistic extends AbstractModel implements IdentityInterface
{
    /**
     * Fastly INSTALLED Flag
     */
    const FASTLY_INSTALLED_FLAG = 'installed';
    /**
     * Fastly CONFIGURED Flag
     */
    const FASTLY_CONFIGURED_FLAG = 'configured';
    /**
     * Fastly NOT_CONFIGURED Flag
     */
    const FASTLY_NOT_CONFIGURED_FLAG = 'not_configured';
    const FASTLY_VALIDATED_FLAG = 'validated';
    const FASTLY_NON_VALIDATED_FLAG = 'non_validated';

    const FASTLY_CONFIGURATION_FLAG = 'configuration';
    const FASTLY_VALIDATION_FLAG = 'validation';

    /**
     * Fastly upgrade flag
     */
    const FASTLY_UPGRADE_FLAG = 'upgrade';
    const FASTLY_UPGRADED_FLAG = 'upgraded';

    const FASTLY_MODULE_NAME = 'Fastly_Cdn';
    const CACHE_TAG = 'fastly_cdn_statistic';
    const FASTLY_GA_TRACKING_ID = 'UA-89025888-1';
    const GA_API_ENDPOINT = 'https://www.google-analytics.com/collect';
    const GA_HITTYPE_PAGEVIEW = 'pageview';
    const GA_HITTYPE_EVENT = 'event';
    const GA_PAGEVIEW_URL = 'http://fastly.com/';
    const GA_FASTLY_SETUP = 'Fastly Setup';
    /**
     * @var array
     */
    private $GAReqData = [];
    /**
     * @var null|string
     */
    private $validationServiceId = null;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;
    /**
     * @var ProductMetadataInterface Magento meta data (version)
     */
    private $metaData;
    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;
    /**
     * @var CountryInformationAcquirerInterface
     */
    private $countryInformation;
    /**
     * @var RegionFactory
     */
    private $regionFactory;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var CurlFactory
     */
    private $curlFactory;
    /**
     * @var StatisticRepository
     */
    private $statisticRepository;
    /**
     * @var DateTime
     */
    private $dateTime;
    /**
     * @var Http
     */
    private $request;
    /**
     * @var CountryFactory
     */
    private $countryFactory;
    /**
     * @var Data
     */
    private $helper;

    /**
     * Statistic constructor.
     * @param Context $context
     * @param Registry $registry
     * @param \Fastly\Cdn\Model\Config $config
     * @param StoreManagerInterface $storeManager
     * @param ScopeConfigInterface $scopeConfig
     * @param CountryInformationAcquirerInterface $countryInformation
     * @param RegionFactory $regionFactory
     * @param Api $api
     * @param CurlFactory $curlFactory
     * @param StatisticRepository $statisticRepository
     * @param DateTime $dateTime
     * @param ProductMetadataInterface $productMetadata
     * @param Http $request
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\Model\Context $context,
        Registry $registry,
        Config $config,
        StoreManagerInterface $storeManager,
        ScopeConfigInterface $scopeConfig,
        CountryInformationAcquirerInterface $countryInformation,
        RegionFactory $regionFactory,
        Api $api,
        CurlFactory $curlFactory,
        CountryFactory $countryFactory,
        StatisticRepository $statisticRepository,
        DateTime $dateTime,
        Data $helper,
        ProductMetadataInterface $productMetadata,
        Http $request,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->config = $config;
        $this->storeManager = $storeManager;
        $this->metaData = $productMetadata;
        $this->scopeConfig = $scopeConfig;
        $this->countryInformation = $countryInformation;
        $this->regionFactory = $regionFactory;
        $this->api = $api;
        $this->curlFactory = $curlFactory;
        $this->statisticRepository = $statisticRepository;
        $this->dateTime = $dateTime;
        $this->countryFactory = $countryFactory;
        $this->helper = $helper;
        $this->request = $request;

        parent::__construct($context, $registry, $resource, $resourceCollection, $data);
    }

    protected function _construct() // @codingStandardsIgnoreLine - required by parent class
    {
        $this->_init('Fastly\Cdn\Model\ResourceModel\Statistic');
    }

    public function getIdentities()
    {
        return [self::CACHE_TAG. '_' .$this->getId()];
    }

    /**
     * Returns GA API Endpoint
     *
     * @return string
     */
    public function getApiEndpoint()
    {
        return self::GA_API_ENDPOINT;
    }

    /**
     * Returns GA Tracking ID
     *
     * @return string
     */
    public function getGATrackingId()
    {
        return self::FASTLY_GA_TRACKING_ID;
    }

    /**
     * Prepares GA data for request
     *
     * @return array
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    private function prepareGAReqData()
    {
        if (!empty($this->GAReqData)) {
            return $this->GAReqData;
        }

        $mandatoryReqData = [];
        $mandatoryReqData['v'] = 1;
        // Tracking ID
        $mandatoryReqData['tid'] = $this->getGATrackingId();
        $cid = $this->config->getCID();
        $mandatoryReqData['cid'] = $cid;
        $mandatoryReqData['uid'] = $cid;
        // Magento version
        $mandatoryReqData['ua'] = $this->metaData->getVersion();
        // Get Default Country
        $mandatoryReqData['geoid'] = $this->getCountry();
        // Data Source parameter is used to filter spam hits
        $mandatoryReqData['ds'] = 'Fastly';

        $customVars = $this->prepareCustomVariables();
        $this->GAReqData = array_merge($mandatoryReqData, $customVars);

        return $this->GAReqData;
    }

    /**
     * Returns Website name
     *
     * @return string $websiteName
     */
    public function getWebsiteName()
    {
        $websites = $this->storeManager->getWebsites();

        $websiteName = 'Not set.';

        foreach ($websites as $website) {
            if ($website->getIsDefault()) {
                $websiteName = $website->getName();
            }
        }

        return $websiteName;
    }

    /**
     * Checks if API token is valid
     *
     * @return bool $isApiKeyValid
     */
    public function isApiKeyValid()
    {
        try {
            $apiKey = $this->scopeConfig->getValue(Config::XML_FASTLY_API_KEY);
            $serviceId = $this->scopeConfig->getValue(Config::XML_FASTLY_SERVICE_ID);
            $isApiKeyValid = $this->api->checkServiceDetails(true, $serviceId, $apiKey);
        } catch (\Exception $e) {
            return false;
        }
        return (bool)$isApiKeyValid;
    }

    /**
     * Prepares GA custom variables
     *
     * @return array
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    private function prepareCustomVariables()
    {
        if ($this->validationServiceId != null) {
            $serviceId = $this->validationServiceId;
        } else {
            $serviceId = $this->scopeConfig->getValue(Config::XML_FASTLY_SERVICE_ID);
        }

        $customVars =  [
            // Service ID
            'cd1'   =>  $serviceId,
            // isAPIKeyValid
            'cd2'   =>  ($this->isApiKeyValid()) ? 'yes' : 'no',
            // Website name
            'cd3'   =>  $this->getWebsiteName(),
            // Site domain
            'cd4'   =>  $this->request->getServer('HTTP_HOST'),
            // Site location
            'cd5'   =>  $this->getSiteLocation(),
            // Fastly module version
            'cd6'   =>  $this->helper->getModuleVersion(),
            // Fastly CID
            'cd7'   =>  $this->config->getCID(),
            // Anti spam protection
            'cd8'   =>  'fastlyext'
        ];

        return $customVars;
    }

    /**
     * Returns default Country
     *
     * @return string
     */
    public function getCountry()
    {
        $countryCode = $this->scopeConfig->getValue('general/country/default');
        if (!$countryCode) {
            return null;
        }

        $country = $this->countryFactory->create()->loadByCode($countryCode);

        return $country->getName();
    }

    /**
     * Get Default Site Location
     *
     * @return string
     */
    public function getSiteLocation()
    {
        $countryId = $this->scopeConfig->getValue('general/store_information/country_id');
        if ($countryId) {
            $country = $this->countryFactory->create()->loadByCode($countryId);
            $countryName = $country->getName();
        } else {
            $countryName = 'Unknown country';
        }

        $regionId = $this->scopeConfig->getValue('general/store_information/region_id');
        $regionName = 'Unknown region';
        if ($regionId) {
            $region = $this->regionFactory->create();
            $region = $region->load($regionId);
            if ($region->getId()) {
                $regionName = $region->getName();
            }
        }

        $postCode = $this->scopeConfig->getValue('general/store_information/postcode');
        if (!$postCode) {
            $postCode = 'Unknown zip code';
        }

        return $countryName .' | '.$regionName.' | '.$postCode;
    }

    /**
     * Generate GA CID
     *
     * @return string
     */
    public function generateCid()
    {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            /* 32 bits for time_low */
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            /* 16 bits for time_mid */
            mt_rand(0, 0xffff),
            /* 16 bits for time_hi_and_version,
               four most significant bits holds version number 4 */
            mt_rand(0, 0x0fff) | 0x4000,
            /* 16 bits, 8 bits for clk_seq_hi_res,
               8 bits for clk_seq_low,
               two most significant bits holds zero and one for variant DCE1.1 */
            mt_rand(0, 0x3fff) | 0x8000,
            // 48 bits for node
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0xffff)
        );
    }

    /**
     * Get Google Analytics mandatory data
     *
     * @return array
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getGAReqData()
    {
        return $this->prepareGAReqData();
    }

    /**
     * Sends request to GA that the Fastly module is installed
     *
     * @return bool|string $result
     */
    public function sendInstalledReq()
    {
        $pageViewParams = [
            'dl'    =>  self::GA_PAGEVIEW_URL . self::FASTLY_INSTALLED_FLAG,
            'dh'    =>  preg_replace('#^https?://#', '', rtrim(self::GA_PAGEVIEW_URL, '/')),
            'dp'    =>  '/'.self::FASTLY_INSTALLED_FLAG,
            'dt'    =>  ucfirst(self::FASTLY_INSTALLED_FLAG),
            't'     =>  self::GA_HITTYPE_PAGEVIEW,
        ];

        $this->sendReqToGA($pageViewParams, self::GA_HITTYPE_PAGEVIEW);

        $eventParams = [
            'ec'    =>  self::GA_FASTLY_SETUP,
            'ea'    =>  'Fastly '.self::FASTLY_INSTALLED_FLAG,
            'el'    =>  $this->getWebsiteName(),
            'ev'    =>  0,
            't'     =>  self::GA_HITTYPE_EVENT
        ];

        $result = $this->sendReqToGA(array_merge($pageViewParams, $eventParams));

        return $result;
    }

    /**
     * Sends request to GA every time the Test connection button is pressed
     *
     * @param $validatedFlag
     * @param null $serviceId
     * @return bool
     */
    public function sendValidationRequest($validatedFlag, $serviceId = null)
    {
        if ($serviceId != null) {
            $this->validationServiceId = $serviceId;
        }

        if ($validatedFlag) {
            $validationState = self::FASTLY_VALIDATED_FLAG;
        } else {
            $validationState = self::FASTLY_NON_VALIDATED_FLAG;
        }

        $pageViewParams = [
            'dl'    =>  self::GA_PAGEVIEW_URL . $validationState,
            'dh'    =>  preg_replace('#^https?://#', '', rtrim(self::GA_PAGEVIEW_URL, '/')),
            'dp'    =>  '/'.$validationState,
            'dt'    =>  ucfirst($validationState),
            't'     =>  self::GA_HITTYPE_PAGEVIEW,
        ];

        $this->sendReqToGA($pageViewParams);

        $eventParams = [
            'ec'    =>  self::GA_FASTLY_SETUP,
            'ea'    =>  'Fastly '.$validationState,
            'el'    =>  $this->getWebsiteName(),
            'ev'    =>  $this->daysFromInstallation(),
            't'     =>  self::GA_HITTYPE_EVENT
        ];

        $result = $this->sendReqToGA(array_merge($pageViewParams, $eventParams));

        return $result;
    }

    public function sendUpgradeRequest()
    {
        $pageViewParams = [
            'dl'    =>  self::GA_PAGEVIEW_URL . self::FASTLY_UPGRADED_FLAG,
            'dh'    =>  preg_replace('#^https?://#', '', rtrim(self::GA_PAGEVIEW_URL, '/')),
            'dp'    =>  '/'.self::FASTLY_UPGRADED_FLAG,
            'dt'    =>  ucfirst(self::FASTLY_UPGRADED_FLAG),
            't'     =>  self::GA_HITTYPE_PAGEVIEW,
        ];

        $this->sendReqToGA($pageViewParams);

        $eventParams = [
            'ec'    =>  self::GA_FASTLY_SETUP,
            'ea'    =>  'Fastly '.self::FASTLY_UPGRADED_FLAG,
            'el'    =>  $this->getWebsiteName(),
            'ev'    =>  $this->daysFromInstallation(),
            't'     =>  self::GA_HITTYPE_EVENT
        ];

        $result = $this->sendReqToGA(array_merge($pageViewParams, $eventParams));

        return $result;
    }

    /**
     * Sends Fastly configured\not_configured request to GA
     *
     * @param $configuredFlag
     * @return bool
     */
    public function sendConfigurationRequest($configuredFlag)
    {
        if ($configuredFlag) {
            $configuredState = self::FASTLY_CONFIGURED_FLAG;
        } else {
            $configuredState = self::FASTLY_NOT_CONFIGURED_FLAG;
        }

        $pageViewParams = [
            'dl'    =>  self::GA_PAGEVIEW_URL . $configuredState,
            'dh'    =>  preg_replace('#^https?://#', '', rtrim(self::GA_PAGEVIEW_URL, '/')),
            'dp'    =>  '/'.$configuredState,
            'dt'    =>  ucfirst($configuredState),
            't'     =>  self::GA_HITTYPE_PAGEVIEW,
        ];

        $this->sendReqToGA($pageViewParams);

        $eventParams = [
            'ec'    =>  self::GA_FASTLY_SETUP,
            'ea'    =>  'Fastly '.$configuredState,
            'el'    =>  $this->getWebsiteName(),
            'ev'    =>  $this->daysFromInstallation(),
            't'     =>  self::GA_HITTYPE_EVENT
        ];

        $result = $this->sendReqToGA(array_merge($pageViewParams, $eventParams));

        return $result;
    }

    /**
     * Calculates number of days since Fastly module installation
     *
     * @return mixed|null
     */
    public function daysFromInstallation()
    {
        $stat = $this->statisticRepository->getStatByAction(self::FASTLY_INSTALLED_FLAG);

        if (!$stat->getCreatedAt()) {
            return null;
        }
        $installDate = date_create($stat->getCreatedAt());
        $currentDate = date_create($this->dateTime->gmtDate());

        $dateDiff = date_diff($installDate, $currentDate);

        return $dateDiff->days;
    }

    /**
     * Sends CURL request to GA
     *
     * @param string $body
     * @param string $method
     * @param string $uri
     * @return bool
     */
    private function sendReqToGA($body = '', $method = \Zend_Http_Client::POST, $uri = self::GA_API_ENDPOINT)
    {
        $reqGAData = (array)$this->getGAReqData();

        if ($body != '' && is_array($body) && !empty($body)) {
            $body = array_merge($reqGAData, $body);
        }

        try {
            $client = $this->curlFactory->create();
            $client->addOption(CURLOPT_TIMEOUT, 10);
            $client->write($method, $uri, '1.1', null, http_build_query($body));
            $response = $client->read();
            $responseCode = \Zend_Http_Response::extractCode($response);
            $client->close();

            if ($responseCode != '200') {
                throw new LocalizedException(__('Return status ' . $responseCode));
            }

            return true;
        } catch (\Exception $e) {
            return false;
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Block\GeoIp;

use Fastly\Cdn\Model\Config;
use Magento\Framework\View\Element\AbstractBlock;
use Magento\Framework\View\Element\Context;
use Magento\Framework\App\ResponseInterface as Response;

/**
 * Class GetAction
 *
 * @package Fastly\Cdn\Block\GeoIp
 */
class GetAction extends AbstractBlock
{
    /**
     * @var Config
     */
    private $config;

    /**
     * @var Response
     */
    private $response;

    /**
     * GetAction constructor.
     *
     * @param Config $config
     * @param Context $context
     * @param Response $response
     * @param array $data
     */
    public function __construct(
        Config $config,
        Context $context,
        Response $response,
        array $data = []
    ) {
        $this->config = $config;
        $this->response = $response;

        parent::__construct($context, $data);
    }

    /**
     * Renders ESI GeoIp block
     *
     * @return string
     */
    protected function _toHtml() // @codingStandardsIgnoreLine - required by parent class
    {
        if ($this->config->isGeoIpEnabled() == false || $this->config->isFastlyEnabled() == false) {
            return parent::_toHtml();
        }

        /** @var string $actionUrl */
        $actionUrl = $this->getUrl('fastlyCdn/geoip/getaction');

        // This page has an esi tag, set x-esi header if it is not already set
        $header = $this->response->getHeader('x-esi');
        if (empty($header)) {
            $this->response->setHeader("x-esi", "1");
        }

        // HTTPS ESIs are not supported so we need to turn them into HTTP
        return sprintf(
            '<esi:include src=\'%s\' />',
            preg_replace("/^https/", "http", $actionUrl)
        );
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Block\System\Config\Form\Field;

use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
use Magento\Framework\Data\Form\Element\Factory;

/**
 * Class Acl
 *
 * @package Fastly\Cdn\Block\System\Config\Form\Field
 */
class Acl extends AbstractFieldArray
{
    /**
     * @var Factory
     */
    private $elementFactory;

    /**
     * Acl constructor.
     *
     * @param Context $context
     * @param Factory $elementFactory
     * @param array $data
     */
    public function __construct(
        Context $context,
        Factory $elementFactory,
        array $data = []
    ) {
        $this->elementFactory = $elementFactory;

        parent::__construct($context, $data);
    }

    /**
     * Initialise form fields
     *
     * @return void
     */
    protected function _construct() // @codingStandardsIgnoreLine - required by parent class
    {
        $this->addColumn('backend_name', ['label' => __('Name')]);
        $this->_addAfter = false;
        $this->_template = 'Fastly_Cdn::system/config/form/field/acl.phtml';

        parent::_construct();
    }

    /**
     * Render array cell for prototypeJS template
     *
     * @param string $columnName
     * @return mixed|string
     * @throws \Exception
     */
    public function renderCellTemplate($columnName)
    {
        if ($columnName == 'store_id' && isset($this->_columns[$columnName])) {
            $options = $this->getOptions(__('-- Select Store --'));
            $element = $this->elementFactory->create('select');
            $element->setForm(
                $this->getForm()
            )->setName(
                $this->_getCellInputElementName($columnName)
            )->setHtmlId(
                $this->_getCellInputElementId('<%- _id %>', $columnName)
            )->setValues(
                $options
            );
            return str_replace("\n", '', $element->getElementHtml());
        }

        return parent::renderCellTemplate($columnName);
    }

    /**
     * Get list of store views.
     *
     * @param bool|false $label
     * @return array
     */
    protected function getOptions($label = false) // @codingStandardsIgnoreLine - required by parent class
    {
        $options = [];
        foreach ($this->_storeManager->getStores() as $store) {
            $options[] = [
                'value' => $store->getId(),
                'label' => $store->getName()
            ];
        }

        if ($label) {
            array_unshift($options, [
                'value' => '',
                'label' => $label
            ]);
        }

        return $options;
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Model\System\Config\Source\GeoIP;

/**
 * Class Action
 *
 * @package Fastly\Cdn\Model\System\Config\Source\GeoIP
 */
class Action implements \Magento\Framework\Data\OptionSourceInterface
{
    /**
     * Options getter
     *
     * @return array
     */
    public function toOptionArray()
    {
        return [
            [
                'value' => \Fastly\Cdn\Model\Config::GEOIP_ACTION_DIALOG,
                'label' => __('Dialog'),
            ],
            [
                'value' => \Fastly\Cdn\Model\Config::GEOIP_ACTION_REDIRECT,
                'label' => __('Redirect')
            ]
        ];
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Dictionary\Item;

use Fastly\Cdn\Model\Api;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Helper\Vcl;

/**
 * Class Delete
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Dictionary\Item
 */
class Delete extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;

    /**
     * ForceTls constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;

        parent::__construct($context);
    }

    /**
     * Delete dictionary item
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $dictionaryId = $this->getRequest()->getParam('dictionary_id');
            $key = $this->getRequest()->getParam('item_key');

            if ($key == '') {
                return $result->setData(['status' => true]);
            }

            $deleteItem = $this->api->deleteDictionaryItem($dictionaryId, $key);

            if (!$deleteItem) {
                return $result->setData(['status' => false]);
            }

            return $result->setData(['status' => true]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Api;

/**
 * Class Apply
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn
 */
class Apply extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Api
     */
    private $api;
    /**
     * Apply constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Api $api
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Api $api
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->api = $api;

        parent::__construct($context);
    }

    /**
     * Apply range
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();
        $from = $this->getRequest()->getParam('from');
        $to = $this->getRequest()->getParam('to');
        $sampleRate = $this->getRequest()->getParam('sample_rate');
        $region = $this->getRequest()->getParam('region');

        if (!$from || !$to) {
            return $result->setData([
                'status'    => false,
                'msg'       => 'Please enter dates.'
            ]);
        }

        /* Convert datetime to timestamp */
        $fromTimestamp = strtotime($from);
        $toTimestamp = strtotime($to);

        /* Parameters array */
        $parameters = [];
        $parameters['from'] = $fromTimestamp;
        $parameters['to'] = $toTimestamp;
        $parameters['sample_rate'] = $sampleRate;
        $parameters['region'] = $region;

        $queryResult = $this->api->queryHistoricStats($parameters);

        return $result->setData([
            'status'    => true,
            'stats'     => $queryResult
        ]);
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl\Item;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Vcl;

/**
 * Class Create
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl\Item
 */
class Create extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;

    /**
     * Create constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;

        parent::__construct($context);
    }

    /**
     * Create ACL entry for specific ACL
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $aclId = $this->getRequest()->getParam('acl_id');
            $value = $this->getRequest()->getParam('item_value');
            $comment = $this->getRequest()->getParam('comment_value');
            $negated = 0;

            if ($value[0] == '!') {
                $negated = 1;
                $value = ltrim($value, '!');
            }

            // Handle subnet
            $ipParts = explode('/', $value);
            $subnet = false;
            if (!empty($ipParts[1])) {
                if (is_numeric($ipParts[1]) && (int)$ipParts[1] < 129) {
                    $subnet = $ipParts[1];
                } else {
                    return $result->setData([
                        'status'    => false,
                        'msg'       => 'Invalid IP subnet format.'
                    ]);
                }
            }

            if (!filter_var($ipParts[0], FILTER_VALIDATE_IP)) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'Invalid IP address format.'
                ]);
            }

            $createAclItem = $this->api->upsertAclItem($aclId, $ipParts[0], $negated, $comment, $subnet);

            if (!$createAclItem) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'Failed to create Acl entry.'
                ]);
            }

            return $result->setData([
                'status'        => true,
                'id'            => $createAclItem->id,
                'comment'       => $createAclItem->comment,
                'created_at'    => $createAclItem->created_at
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Blocking;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Vcl;
use Magento\Framework\App\Config\Storage\WriterInterface as ConfigWriter;
use Magento\Framework\App\Cache\TypeListInterface as CacheTypeList;
use Magento\Config\App\Config\Type\System as SystemConfig;

/**
 * Class UpdateBlocking
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Blocking
 */
class UpdateBlocking extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;
    /**
     * @var ConfigWriter
     */
    private $configWriter;
    /**
     * @var CacheTypeList
     */
    private $cacheTypeList;
    /**
     * @var SystemConfig
     */
    private $systemConfig;

    /**
     * UpdateBlocking constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     * @param ConfigWriter $configWriter
     * @param CacheTypeList $cacheTypeList
     * @param SystemConfig $systemConfig
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl,
        ConfigWriter $configWriter,
        CacheTypeList $cacheTypeList,
        SystemConfig $systemConfig
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;
        $this->configWriter = $configWriter;
        $this->cacheTypeList = $cacheTypeList;
        $this->systemConfig = $systemConfig;
        parent::__construct($context);
    }

    /**
     * Upload Blocking snippets
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();
        try {
            $service = $this->api->checkServiceDetails();
            $currActiveVersion = $this->vcl->determineVersions($service->versions);

            $snippet = $this->config->getVclSnippets(
                Config::VCL_BLOCKING_PATH,
                Config::VCL_BLOCKING_SNIPPET
            );

            $country_codes = $this->prepareCountryCodes($this->request->getParam('countries'));
            $acls = $this->prepareAcls($this->request->getParam('acls'));

            $blockedItems = $country_codes . $acls;
            $strippedBlockedItems = substr($blockedItems, 0, strrpos($blockedItems, '||', -1));
            $blockingType = $this->request->getParam('blocking_type');

            $this->configWriter->save(
                Config::XML_FASTLY_BLOCKING_TYPE,
                $blockingType,
                'default',
                '0'
            );

            // Add blocking snippet
            foreach ($snippet as $key => $value) {
                if ($strippedBlockedItems === '') {
                    $value = '';
                } else {
                    $strippedBlockedItems = $this->config->processBlockedItems($strippedBlockedItems, $blockingType);
                    $value = str_replace('####BLOCKED_ITEMS####', $strippedBlockedItems, $value);
                }

                $snippetName = Config::FASTLY_MAGENTO_MODULE . '_blocking_' . $key;
                $snippetId = $this->api->getSnippet($currActiveVersion['active_version'], $snippetName)->id;
                $params = [
                    'name'      =>  $snippetId,
                    'content'   => $value
                ];

                $this->api->updateSnippet($params);
            }

            $this->cacheTypeList->cleanType('config');
            $this->systemConfig->clean();

            return $result->setData([
                'status' => true
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }

    /**
     * Prepares ACL VCL snippets
     *
     * @param $blockedAcls
     * @return string
     */
    private function prepareAcls($blockedAcls)
    {
        $result = '';
        $aclsArray = [];
        $acls = '';

        if ($blockedAcls != null) {
            foreach ($blockedAcls as $key => $value) {
                $aclsArray[] = $value['value'];
            }
            $acls = implode(',', $aclsArray);
        }

        $this->configWriter->save(
            Config::XML_FASTLY_BLOCK_BY_ACL,
            $acls,
            'default',
            '0'
        );

        if ($acls != '') {
            $blockedAclsPieces = explode(",", $acls);
            foreach ($blockedAclsPieces as $acl) {
                $result .= ' req.http.Fastly-Client-Ip ~ ' . $acl . ' ||';
            }
        }

        return $result;
    }

    /**
     * Prepares blocked countries VCL snippet
     *
     * @param $blockedCountries
     * @return string
     */
    private function prepareCountryCodes($blockedCountries)
    {
        $result = '';
        $countriesArray = [];
        $countries = '';

        if ($blockedCountries != null) {
            foreach ($blockedCountries as $key => $value) {
                $countriesArray[] = $value['value'];
            }
            $countries = implode(',', $countriesArray);
        }

        $this->configWriter->save(
            Config::XML_FASTLY_BLOCK_BY_COUNTRY,
            $countries,
            'default',
            '0'
        );

        if ($countries != '') {
            $blockedCountriesPieces = explode(",", $countries);
            foreach ($blockedCountriesPieces as $code) {
                $result .= ' client.geo.country_code == "' . $code . '" ||';
            }
        }

        return $result;
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\CustomSnippet;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\RawFactory;
use Magento\Framework\App\Response\Http\FileFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem\Directory\WriteFactory;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Filesystem;

/**
 * Class GetCustomSnippets
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\CustomSnippet
 */
class GetCustomSnippets extends Action
{
    /**
     * @var RawFactory
     */
    private $resultRawFactory;
    /**
     * @var FileFactory
     */
    private $fileFactory;
    /**
     * @var DirectoryList
     */
    private $directoryList;
    /**
     * @var WriteFactory
     */
    private $writeFactory;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Filesystem
     */
    private $filesystem;

    /**
     * GetCustomSnippets constructor.
     *
     * @param Context $context
     * @param RawFactory $resultRawFactory
     * @param FileFactory $fileFactory
     * @param DirectoryList $directoryList
     * @param WriteFactory $writeFactory
     * @param JsonFactory $resultJsonFactory
     * @param Filesystem $filesystem
     */
    public function __construct(
        Context $context,
        RawFactory $resultRawFactory,
        FileFactory $fileFactory,
        DirectoryList $directoryList,
        WriteFactory $writeFactory,
        JsonFactory $resultJsonFactory,
        Filesystem $filesystem
    ) {
        $this->resultRawFactory = $resultRawFactory;
        $this->fileFactory = $fileFactory;
        $this->directoryList = $directoryList;
        $this->writeFactory = $writeFactory;
        $this->resultJson = $resultJsonFactory;
        $this->filesystem = $filesystem;

        parent::__construct($context);
    }

    /**
     * Get custom snippets
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();
        try {
            $read = $this->filesystem->getDirectoryRead(DirectoryList::VAR_DIR);
            $snippetPath = $read->getRelativePath('vcl_snippets_custom');

            $customSnippets = $read->read($snippetPath);

            if (!$customSnippets) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'No snippets found.'
                ]);
            }
            $snippets = [];
            foreach ($customSnippets as $snippet) {
                $snippets[] = explode('/', $snippet)[1];
            }

            return $result->setData([
                'status'    => true,
                'snippets'  => $snippets
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Model;

/**
 * Class PurgeCache
 *
 * @package Fastly\Cdn\Model
 */
class PurgeCache
{
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Config
     */
    private $config;

    /**
     * Constructor
     *
     * @param Api $api
     * @param Config $config
     */
    public function __construct(Api $api, Config $config)
    {
        $this->api = $api;
        $this->config = $config;
    }

    /**
     * Send API purge request to invalidate cache by pattern
     *
     * @param string $pattern
     * @return bool
     * @throws \Zend_Uri_Exception
     */
    public function sendPurgeRequest($pattern = '')
    {
        if (empty($pattern)) {
            if ($this->config->canPreserveStatic()) {
                $result = $this->api->cleanBySurrogateKey(['text']);
            } else {
                $result = $this->api->cleanAll();
            }
        } elseif (!is_array($pattern) && strpos($pattern, 'http') === 0) {
            $result = $this->api->cleanUrl($pattern);
        } elseif (is_array($pattern)) {
            $result = $this->api->cleanBySurrogateKey($pattern);
        } else {
            return false;
        }
        return $result;
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Vcl;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Vcl;
use Fastly\Cdn\Model\Config\Backend\CustomSnippetUpload;
use Magento\Framework\Stdlib\DateTime\DateTime;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\App\Filesystem\DirectoryList;

/**
 * Class Upload
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Vcl
 */
class Upload extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;
    /**
     * @var CustomSnippetUpload
     */
    private $customSnippetUpload;
    /**
     * @var DateTime
     */
    private $time;
    /**
     * @var TimezoneInterface
     */
    private $timezone;
    /**
     * @var Filesystem
     */
    private $filesystem;

    /**
     * Upload constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     * @param CustomSnippetUpload $customSnippetUpload
     * @param DateTime $time
     * @param TimezoneInterface $timezone
     * @param Filesystem $filesystem
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl,
        CustomSnippetUpload $customSnippetUpload,
        DateTime $time,
        TimezoneInterface $timezone,
        Filesystem $filesystem
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;
        $this->customSnippetUpload = $customSnippetUpload;
        $this->time = $time;
        $this->timezone = $timezone;
        $this->filesystem = $filesystem;
        parent::__construct($context);
    }

    /**
     * Upload VCL snippets
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();
        try {
            $activeVersion = $this->getRequest()->getParam('active_version');
            $activateVcl = $this->getRequest()->getParam('activate_flag');
            $service = $this->api->checkServiceDetails();
            $this->vcl->checkCurrentVersionActive($service->versions, $activeVersion);
            $currActiveVersion = $this->vcl->getCurrentVersion($service->versions);
            $clone = $this->api->cloneVersion($currActiveVersion);
            $snippets = $this->config->getVclSnippets();
            $read = $this->filesystem->getDirectoryRead(DirectoryList::VAR_DIR);
            $customSnippetPath = $read->getAbsolutePath(Config::CUSTOM_SNIPPET_PATH);
            $customSnippets = $this->config->getCustomSnippets($customSnippetPath);

            foreach ($snippets as $key => $value) {
                $priority = 50;
                if ($key == 'hash') {
                    $priority = 80;
                }
                $snippetData = [
                    'name'      => Config::FASTLY_MAGENTO_MODULE . '_' . $key,
                    'type'      => $key,
                    'dynamic'   => "0",
                    'priority'  => $priority,
                    'content'   => $value
                ];
                $this->api->uploadSnippet($clone->number, $snippetData);
            }

            foreach ($customSnippets as $key => $value) {
                $snippetNameData = $this->validateCustomSnippet($key);
                $snippetType = $snippetNameData[0];
                $snippetPriority = $snippetNameData[1];
                $snippetShortName = $snippetNameData[2];

                $customSnippetData = [
                    'name'      => Config::FASTLY_MAGENTO_MODULE . '_' . $snippetShortName,
                    'type'      => $snippetType,
                    'priority'  => $snippetPriority,
                    'content'   => $value,
                    'dynamic'   => '0'
                ];
                $this->api->uploadSnippet($clone->number, $customSnippetData);
            }

            $condition = [
                'name'      => Config::FASTLY_MAGENTO_MODULE.'_pass',
                'statement' => 'req.http.x-pass',
                'type'      => 'REQUEST',
                'priority'  => 90
            ];
            $createCondition = $this->api->createCondition($clone->number, $condition);
            $request = [
                'action'            => 'pass',
                'max_stale_age'     => 3600,
                'name'              => Config::FASTLY_MAGENTO_MODULE.'_request',
                'request_condition' => $createCondition->name,
                'service_id'        => $service->id,
                'version'           => $currActiveVersion
            ];

            $this->api->createRequest($clone->number, $request);
            $dictionary = $this->setupDictionary($clone->number, $currActiveVersion);
            $acl = $this->setupAcl($clone->number, $currActiveVersion);

            if (!$dictionary || !$acl) {
                throw new LocalizedException(__('Failed to create Containers'));
            }

            $this->api->validateServiceVersion($clone->number);

            if ($activateVcl === 'true') {
                $this->api->activateVersion($clone->number);
            }

            if ($this->config->areWebHooksEnabled() && $this->config->canPublishConfigChanges()) {
                $this->api->sendWebHook(
                    '*Upload VCL has been initiated and activated in version ' . $clone->number . '*'
                );
            }

            $comment = ['comment' => 'Magento Module uploaded VCL'];
            $this->api->addComment($clone->number, $comment);

            return $result->setData([
                'status'            => true,
                'active_version'    => $clone->number
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }

    /**
     * Validate custom snippet naming convention
     * [vcl_snippet_type]_[priority]_[short_name_description].vcl
     *
     * @param $customSnippet
     * @return array
     * @throws LocalizedException
     */
    private function validateCustomSnippet($customSnippet)
    {
        $snippetName = str_replace(' ', '', $customSnippet);
        $snippetNameData = explode('_', $snippetName, 3);
        $containsEmpty = in_array("", $snippetNameData, true);
        $types = ['init', 'recv', 'hit', 'miss', 'pass', 'fetch', 'error', 'log', 'deliver', 'hash', 'none'];
        $exception = 'Failed to upload VCL snippets. Please make sure the custom VCL snippets
            follow this naming convention: [vcl_snippet_type]_[priority]_[short_name_description].vcl';

        if (count($snippetNameData) < 3) {
            throw new LocalizedException(__($exception));
        }

        $inArray = in_array($snippetNameData[0], $types);
        $isNumeric = is_numeric($snippetNameData[1]);
        $isAlphanumeric = preg_match('/^[\w]+$/', $snippetNameData[2]);

        if ($containsEmpty || !$inArray || !$isNumeric || !$isAlphanumeric) {
            throw new LocalizedException(__($exception));
        }
        return $snippetNameData;
    }

    /**
     * @param $cloneNumber
     * @param $currActiveVersion
     * @return bool|mixed
     * @throws LocalizedException
     */
    private function setupDictionary($cloneNumber, $currActiveVersion)
    {
        $dictionaryName = Config::CONFIG_DICTIONARY_NAME;
        $dictionary = $this->api->getSingleDictionary($currActiveVersion, $dictionaryName);

        if (!$dictionary) {
            $params = ['name' => $dictionaryName];
            $dictionary = $this->api->createDictionary($cloneNumber, $params);
        }
        return $dictionary;
    }

    /**
     * @param $cloneNumber
     * @param $currActiveVersion
     * @return bool|mixed
     * @throws LocalizedException
     */
    private function setupAcl($cloneNumber, $currActiveVersion)
    {
        $aclName = Config::MAINT_ACL_NAME;
        $acl = $this->api->getSingleAcl($currActiveVersion, $aclName);

        if (!$acl) {
            $params = ['name' => $aclName];
            $acl = $this->api->createAcl($cloneNumber, $params);
        }
        return $acl;
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\SyntheticPages;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Vcl;
use Magento\Framework\Exception\LocalizedException;

/**
 * Class SaveWafPage
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\SyntheticPages
 */
class SaveWafPage extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;

    /**
     * SaveErrorPage constructor.
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;

        parent::__construct($context);
    }

    /**
     * Save WAF Page
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $activeVersion = $this->getRequest()->getParam('active_version');
            $activateVcl = $this->getRequest()->getParam('activate_flag');
            $wafContent = $this->getRequest()->getParam('content');
            $wafStatus = $this->getRequest()->getParam('status');
            $wafContentType = $this->getRequest()->getParam('content_type');
            $wafResponse = '';
            if (!$wafContent || !$wafStatus || !$wafContentType) {
                throw new LocalizedException(__('Please fill in the required fields.'));
            }
            $service = $this->api->checkServiceDetails();
            $this->vcl->checkCurrentVersionActive($service->versions, $activeVersion);
            $currActiveVersion = $this->vcl->getCurrentVersion($service->versions);
            $clone = $this->api->cloneVersion($currActiveVersion);

            $http_codes = [
                100 => 'Continue',
                101 => 'Switching Protocols',
                102 => 'Processing',
                103 => 'Checkpoint',
                200 => 'OK',
                201 => 'Created',
                202 => 'Accepted',
                203 => 'Non-Authoritative Information',
                204 => 'No Content',
                205 => 'Reset Content',
                206 => 'Partial Content',
                207 => 'Multi-Status',
                300 => 'Multiple Choices',
                301 => 'Moved Permanently',
                302 => 'Found',
                303 => 'See Other',
                304 => 'Not Modified',
                305 => 'Use Proxy',
                306 => 'Switch Proxy',
                307 => 'Temporary Redirect',
                400 => 'Bad Request',
                401 => 'Unauthorized',
                402 => 'Payment Required',
                403 => 'Forbidden',
                404 => 'Not Found',
                405 => 'Method Not Allowed',
                406 => 'Not Acceptable',
                407 => 'Proxy Authentication Required',
                408 => 'Request Timeout',
                409 => 'Conflict',
                410 => 'Gone',
                411 => 'Length Required',
                412 => 'Precondition Failed',
                413 => 'Request Entity Too Large',
                414 => 'Request-URI Too Long',
                415 => 'Unsupported Media Type',
                416 => 'Requested Range Not Satisfiable',
                417 => 'Expectation Failed',
                418 => 'I\'m a teapot',
                422 => 'Unprocessable Entity',
                423 => 'Locked',
                424 => 'Failed Dependency',
                425 => 'Unordered Collection',
                426 => 'Upgrade Required',
                449 => 'Retry With',
                450 => 'Blocked by Windows Parental Controls',
                500 => 'Internal Server Error',
                501 => 'Not Implemented',
                502 => 'Bad Gateway',
                503 => 'Service Unavailable',
                504 => 'Gateway Timeout',
                505 => 'HTTP Version Not Supported',
                506 => 'Variant Also Negotiates',
                507 => 'Insufficient Storage',
                509 => 'Bandwidth Limit Exceeded',
                510 => 'Not Extended'
            ];

            foreach ($http_codes as $key => $value) {
                if ($key == $wafStatus) {
                    $wafResponse = $value;
                    break;
                }
            }

            $response = [
                'name'              => Config::WAF_PAGE_RESPONSE_OBJECT,
                'content'           =>  $wafContent,
                'status'            =>  $wafStatus,
                'content_type'      =>  $wafContentType,
                'response'          =>  $wafResponse
            ];

            $createResponse = $this->api->createResponse($clone->number, $response);

            if (!$createResponse) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'Failed to create a RESPONSE object.'
                ]);
            }

            $this->api->validateServiceVersion($clone->number);

            if ($activateVcl === 'true') {
                $this->api->activateVersion($clone->number);
            }

            if ($this->config->areWebHooksEnabled() && $this->config->canPublishConfigChanges()) {
                $this->api->sendWebHook(
                    '*New WAF page has updated and activated under config version ' . $clone->number . '*'
                );
            }

            $comment = ['comment' => 'Magento Module updated WAF Page html'];
            $this->api->addComment($clone->number, $comment);

            return $result->setData([
                'status'            => true,
                'active_version'    => $clone->number
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\Dashboard;

use Magento\Backend\Controller\Adminhtml\Dashboard\AjaxBlock;

/**
 * Class Historic
 *
 * @package Fastly\Cdn\Controller\Adminhtml\Dashboard
 */
class Historic extends AjaxBlock
{
    /**
     * Return historic stats
     *
     * @return \Magento\Framework\Controller\Result\Raw
     */
    public function execute()
    {
        $output = $this->layoutFactory->create()
            ->createBlock('Fastly\Cdn\Block\Dashboard\Tab\Stats\Historic')
            ->toHtml();
        $resultRaw = $this->resultRawFactory->create();
        return $resultRaw->setContents($output);
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\CustomSnippet;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\Response\Http\FileFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem\Directory\WriteFactory;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Filesystem;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Vcl;

/**
 * Class DeleteCustomSnippet
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\CustomSnippet
 */
class DeleteCustomSnippet extends Action
{
    /**
     * @var FileFactory
     */
    private $fileFactory;
    /**
     * @var DirectoryList
     */
    private $directoryList;
    /**
     * @var WriteFactory
     */
    private $writeFactory;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Filesystem
     */
    private $filesystem;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;

    /**
     * DeleteCustomSnippet constructor.
     * @param Context $context
     * @param FileFactory $fileFactory
     * @param DirectoryList $directoryList
     * @param WriteFactory $writeFactory
     * @param JsonFactory $resultJsonFactory
     * @param Filesystem $filesystem
     * @param Api $api
     * @param Vcl $vcl
     */
    public function __construct(
        Context $context,
        FileFactory $fileFactory,
        DirectoryList $directoryList,
        WriteFactory $writeFactory,
        JsonFactory $resultJsonFactory,
        Filesystem $filesystem,
        Api $api,
        Vcl $vcl
    ) {
        $this->fileFactory = $fileFactory;
        $this->directoryList = $directoryList;
        $this->writeFactory = $writeFactory;
        $this->resultJson = $resultJsonFactory;
        $this->filesystem = $filesystem;
        $this->api = $api;
        $this->vcl = $vcl;

        parent::__construct($context);
    }

    /**
     * Deletes the specified custom snippet VCL file
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $activeVersion = $this->getRequest()->getParam('active_version');
            $snippet = $this->getRequest()->getParam('snippet_id');
            $activateVcl = $this->getRequest()->getParam('activate_flag');
            $service = $this->api->checkServiceDetails();
            $this->vcl->checkCurrentVersionActive($service->versions, $activeVersion);
            $currActiveVersion = $this->vcl->getCurrentVersion($service->versions);

            $write = $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
            $snippetPath = $write->getRelativePath(Config::CUSTOM_SNIPPET_PATH . $snippet);

            $snippetName = explode('_', $snippet);
            $snippetName = explode('.', $snippetName[2]);

            $reqName = Config::FASTLY_MAGENTO_MODULE . '_' . $snippetName[0];
            $checkIfSnippetExist = $this->api->hasSnippet($activeVersion, $reqName);

            if ($checkIfSnippetExist) {
                $clone = $this->api->cloneVersion($currActiveVersion);
                $this->api->removeSnippet($clone->number, $reqName);
                $this->api->validateServiceVersion($clone->number);

                if ($activateVcl === 'true') {
                    $this->api->activateVersion($clone->number);
                }

                $comment = ['comment' => 'Magento Module deleted the ' . $reqName . ' custom snippet.'];
                $this->api->addComment($clone->number, $comment);
            }

            if ($write->isExist($snippetPath)) {
                $write->delete($snippetPath);
            }

            return $result->setData([
                'status'    => true
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl;

use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Fastly\Cdn\Model\Api;
use Fastly\Cdn\Helper\Acl;

/**
 * Class GetAcl
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl
 */
class GetAcl extends Action
{
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Acl
     */
    private $acl;

    /**
     * GetAcl constructor.
     * @param Context $context
     * @param JsonFactory $resultJsonFactory
     * @param Api $api
     * @param Acl $acl
     */
    public function __construct(
        Context $context,
        JsonFactory $resultJsonFactory,
        Api $api,
        Acl $acl
    ) {
        $this->resultJson = $resultJsonFactory;
        $this->api = $api;
        $this->acl = $acl;
        parent::__construct($context);
    }

    /**
     * Get ACL
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $service = $this->api->checkServiceDetails();
            if (!$service) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'Failed to check Service details.'
                ]);
            }

            return $result->$this->acl->determineVersions($service->versions);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Model;

use Magento\AdminNotification\Model\Feed;
use Magento\AdminNotification\Model\InboxFactory;
use Magento\Backend\App\ConfigInterface;
use Magento\Framework\App\Cache\Manager;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\HTTP\Adapter\CurlFactory;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;
use Magento\Framework\UrlInterface;

/**
 * Class Notification
 *
 * Fastly CDN admin notification for latest version
 * @package Fastly\Cdn\Model
 */
class Notification extends Feed
{
    /**
     * Github latest composer data url
     */
    const CHECK_VERSION_URL = 'https://raw.githubusercontent.com/fastly/fastly-magento2/master/composer.json';
    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;
    /**
     * @var WriterInterface
     */
    private $configWriter;
    /**
     * @var Manager
     */
    private $cacheManager;

    /**
     * Notification constructor.
     *
     * @param Context $context
     * @param Registry $registry
     * @param ConfigInterface $backendConfig
     * @param InboxFactory $inboxFactory
     * @param CurlFactory $curlFactory
     * @param DeploymentConfig $deploymentConfig
     * @param ProductMetadataInterface $productMetadata
     * @param UrlInterface $urlBuilder
     * @param ScopeConfigInterface $scopeConfig
     * @param WriterInterface $configWriter
     * @param Manager $cacheManager
     * @param AbstractResource|null $resource
     * @param AbstractDb|null $resourceCollection
     * @param array $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ConfigInterface $backendConfig,
        InboxFactory $inboxFactory,
        CurlFactory $curlFactory,
        DeploymentConfig $deploymentConfig,
        ProductMetadataInterface $productMetadata,
        UrlInterface $urlBuilder,
        ScopeConfigInterface $scopeConfig,
        WriterInterface $configWriter,
        Manager $cacheManager,
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->scopeConfig = $scopeConfig;
        $this->configWriter = $configWriter;
        $this->cacheManager = $cacheManager;
        $this->_logger = $context->getLogger();

        parent::__construct(
            $context,
            $registry,
            $backendConfig,
            $inboxFactory,
            $curlFactory,
            $deploymentConfig,
            $productMetadata,
            $urlBuilder,
            $resource,
            $resourceCollection,
            $data
        );
    }

    /**
     * Check feed for modification
     *
     * @param null $currentVersion
     */
    public function checkUpdate($currentVersion = null)
    {
        $lastVersion = $this->getLastVersion();

        if (!$lastVersion || version_compare($lastVersion, $currentVersion, '<=')) {
            return;
        }

        $versionPath = Config::XML_FASTLY_LAST_CHECKED_ISSUED_VERSION;
        $oldValue = $this->scopeConfig->getValue($versionPath);

        if (version_compare($oldValue, $lastVersion, '<')) {
            $this->configWriter->save($versionPath, $lastVersion);

            // save last version in db, and notify only if newly fetched last version is greater than stored version
            $inboxFactory = $this->_inboxFactory;
            $inbox = $inboxFactory->create();
            $inbox->addNotice(
                'Fastly CDN',
                "Version $lastVersion is available. You are currently running $currentVersion."
                    . ' Please consider upgrading at your earliest convenience.'
            );
            $this->cacheManager->clean([\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER]);
        }
    }

    /**
     * Fetches last github version
     *
     * @return bool|float
     */
    public function getLastVersion()
    {
        try {
            $url = self::CHECK_VERSION_URL;
            $client = $this->curlFactory->create();

            $client->write(\Zend_Http_Client::GET, $url, '1.1');
            $responseBody = $client->read();
            $client->close();

            $responseCode = \Zend_Http_Response::extractCode($responseBody);
            if ($responseCode !== 200) {
                return false;
            }
            $body = \Zend_Http_Response::extractBody($responseBody);
            $json = json_decode($body);
            $version = !empty($json->version) ? $json->version : false;

            return $version;
        } catch (\Exception $e) {
            $this->_logger->log(100, $e->getMessage().$url);
            return false;
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Dictionary\Item;

use Fastly\Cdn\Model\Api;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Config;
use Fastly\Cdn\Helper\Vcl;

/**
 * Class Create
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Dictionary\Item
 */
class Create extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Config
     */
    private $config;
    /**
     * @var Api
     */
    private $api;
    /**
     * @var Vcl
     */
    private $vcl;

    /**
     * ForceTls constructor.
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Config $config
     * @param Api $api
     * @param Vcl $vcl
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Config $config,
        Api $api,
        Vcl $vcl
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->config = $config;
        $this->api = $api;
        $this->vcl = $vcl;

        parent::__construct($context);
    }

    /**
     * Create dictionary item
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $dictionaryId = $this->getRequest()->getParam('dictionary_id');
            $value = $this->getRequest()->getParam('item_value');
            $key = $this->getRequest()->getParam('item_key');
            $this->api->upsertDictionaryItem($dictionaryId, $key, $value);

            return $result->setData(['status' => true]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl\Item;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\Request\Http;
use Magento\Framework\Controller\Result\JsonFactory;
use Fastly\Cdn\Model\Api;

/**
 * Class ListAll
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Edge\Acl\Item
 */
class ListAll extends Action
{
    /**
     * @var Http
     */
    private $request;
    /**
     * @var JsonFactory
     */
    private $resultJson;
    /**
     * @var Api
     */
    private $api;

    /**
     * ListAll constructor
     *
     * @param Context $context
     * @param Http $request
     * @param JsonFactory $resultJsonFactory
     * @param Api $api
     */
    public function __construct(
        Context $context,
        Http $request,
        JsonFactory $resultJsonFactory,
        Api $api
    ) {
        $this->request = $request;
        $this->resultJson = $resultJsonFactory;
        $this->api = $api;

        parent::__construct($context);
    }

    /**
     * Get all ACL entries for active version and current ACL
     *
     * @return $this|\Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        $result = $this->resultJson->create();

        try {
            $aclId = $this->getRequest()->getParam('acl_id');
            $aclItems = $this->api->aclItemsList($aclId);

            if (is_array($aclItems) && empty($aclItems)) {
                return $result->setData([
                    'status'    => 'empty',
                    'aclItems'  => []
                ]);
            }

            if (!$aclItems) {
                return $result->setData([
                    'status'    => false,
                    'msg'       => 'Failed to fetch acl items.'
                ]);
            }

            return $result->setData([
                'status'    => true,
                'aclItems'  => $aclItems
            ]);
        } catch (\Exception $e) {
            return $result->setData([
                'status'    => false,
                'msg'       => $e->getMessage()
            ]);
        }
    }
}


<?php
/**
 * Fastly CDN for Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Fastly CDN for Magento End User License Agreement
 * that is bundled with this package in the file LICENSE_FASTLY_CDN.txt.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Fastly CDN to newer
 * versions in the future. If you wish to customize this module for your
 * needs please refer to http://www.magento.com for more information.
 *
 * @category    Fastly
 * @package     Fastly_Cdn
 * @copyright   Copyright (c) 2016 Fastly, Inc. (http://www.fastly.com)
 * @license     BSD, see LICENSE_FASTLY_CDN.txt
 */
namespace Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Purge;

use Fastly\Cdn\Model\PurgeCache;
use Fastly\Cdn\Model\Config;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Exception\LocalizedException;
use Magento\Store\Model\StoreManagerInterface;

/**
 * Class Store
 *
 * @package Fastly\Cdn\Controller\Adminhtml\FastlyCdn\Purge
 */
class Store extends Action
{
    /**
     * @var PurgeCache
     */
    private $purgeCache;
    /**
     * @var StoreManagerInterface
     */
    private $storeManager;
    /**
     * @var Config
     */
    private $config;

    /**
     * Store constructor.
     *
     * @param Context $context
     * @param PurgeCache $purgeCache
     * @param StoreManagerInterface $storeManager
     * @param Config $config
     */
    public function __construct(
        Context $context,
        PurgeCache $purgeCache,
        StoreManagerInterface $storeManager,
        Config $config
    ) {
        $this->purgeCache = $purgeCache;
        $this->storeManager = $storeManager;
        $this->config = $config;

        parent::__construct($context);
    }

    /**
     * Purge by content type
     *
     * @return \Magento\Framework\App\ResponseInterface
     * @throws \Exception
     */
    public function execute()
    {
        try {
            if ($this->config->getType() == Config::FASTLY && $this->config->isEnabled()) {
                // check if store exists
                $storeId = $this->getRequest()->getParam('stores', false);
                /** @var \Magento\Store\Model\Store $store */
                $store = $this->storeManager->getStore($storeId);
                if (!$store->getId()) {
                    throw new LocalizedException(__('Invalid store "'.$storeId.'" given.'));
                }

                $result = $this->purgeCache->sendPurgeRequest($store->getIdentities());

                if ($result) {
                    $this->getMessageManager()->addSuccessMessage(__('The Fastly CDN has been cleaned.'));
                } else {
                    $this->getMessageManager()->addErrorMessage(
                        __('The purge request was not processed successfully.')
                    );
                }
            }
        } catch (\Exception $e) {
            $this->getMessageManager()->addErrorMessage(
                __('An error occurred while clearing the Fastly CDN: ') . $e->getMessage()
            );
        }
        return $this->_redirect('*/cache/index');
    }
}


<?php

namespace Fastly\Cdn\Plugin\Config;

use Fastly\Cdn\Model\Modly\Manifest;
use Fastly\Cdn\Model\Modly\NodeFactory;
use Fastly\Cdn\Helper\Manifest as ManifestData;
use Magento\Config\Model\Config\ScopeDefiner;
use Magento\Config\Model\Config\Structure;
use Magento\Config\Model\Config\Structure\Element\Section;
use Magento\Config\Model\Config\Structure\ElementInterface;

class StructurePlugin
{
    /**
     * @var ScopeDefiner
     */
    private $scopeDefiner;

    /**
     * @var NodeFactory
     */
    private $nodeFactory;

    /**
     * @var Manifest
     */
    private $manifest;

    /**
     * @var bool
     */
    private $isLoaded = false;

    private $manifestData;

    /**
     * StructurePlugin constructor.
     *
     * @param ScopeDefiner $scopeDefiner
     * @param NodeFactory $nodeFactory
     * @param Manifest $manifest
     */
    public function __construct(
        ScopeDefiner $scopeDefiner,
        NodeFactory $nodeFactory,
        Manifest $manifest,
        ManifestData $manifestData
    ) {
        $this->scopeDefiner = $scopeDefiner;
        $this->nodeFactory  = $nodeFactory;
        $this->manifest     = $manifest;
        $this->manifestData = $manifestData;
    }

    /**
     * Injects modlyies to configuration
     *
     * @param \Closure $proceed
     * @param array $pathParts
     * @return ElementInterface|null
     *
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function aroundGetElementByPathParts(\Closure $proceed, array $pathParts)
    {
        /** @var Section $result */
        $result = $proceed($pathParts);

        if ($this->isLoaded == true || false) {
            return $result;
        }

        if (($result instanceof Section) == false) {
            return $result;
        }

        if (isset($pathParts[0]) == false || $pathParts[0] != 'system') {
            return $result;
        }

        $this->isLoaded = true;
        $data = $result->getData();

        if (isset($data['children']['full_page_cache']['children']['fastly_edge_modules']['children']) == false) {
            return $result;
        }

        $original = $data['children']['full_page_cache']['children']['fastly_edge_modules']['children'];
        $data['children']['full_page_cache']['children']['fastly_edge_modules']['children'] = array_merge(
            $original,
            $this->loadModlyData()
        );

        $result->setData(
            $data,
            $this->scopeDefiner->getScope()
        );

        return $result;
    }

    private function loadModlyData()
    {
        $sampleData = $this->manifest->getActiveModlyManifests();
        $result = [];

        foreach ($sampleData as $data) {
            $nodeData = json_decode($data['manifest_content']);
            /** @var \Fastly\Cdn\Model\Modly\Node $node */
            $node = $this->nodeFactory->create(
                [
                    'id'        => $nodeData['id'],
                    'label'     => $nodeData['name'],
                    'comment'   => (isset($nodeData['description']) == true)? $nodeData['description'] : ''
                ]
            );
            foreach ($nodeData['properties'] as $property) {
                if (isset($property['options'])) {
                    $node->addOptionInput(
                        $property['name'],
                        $property['label'],
                        (isset($property['description']) == true)? $property['description'] : '',
                        $property['options']
                    );
                } else {
                    $node->addTextInput(
                        $property['name'],
                        $property['label'],
                        (isset($property['description']) == true)? $property['description'] : ''
                    );
                }
            }

            $result[$node->getId()] = $node->render();
        }

        return $result;
    }
}