├── .gitignore ├── view └── adminhtml │ ├── web │ ├── js │ │ └── .DS_Store │ ├── assets │ │ ├── .DS_Store │ │ ├── layers.png │ │ ├── spinner.gif │ │ ├── throbber.gif │ │ ├── layers-2x.png │ │ ├── select2-cf.png │ │ ├── select2x2-cf.png │ │ ├── details-arrows.png │ │ ├── hero-bg-clouds.png │ │ ├── icon-bolt.svg │ │ ├── vertical-range.png │ │ ├── yjs-background.jpg │ │ ├── icons-seee324dde5.png │ │ ├── select2-cf-white.png │ │ ├── select2x2-cf-white.png │ │ ├── yjs-background_2x.jpg │ │ ├── icons_2x-s6333fe7591.png │ │ ├── modal-two-factor-auth.png │ │ ├── icon-shield.svg │ │ ├── modal-two-factor-auth_2x.png │ │ ├── icon-lock.svg │ │ ├── icon-pin.svg │ │ ├── request-submitted-success.svg │ │ ├── plan-changed-success.svg │ │ ├── analytics-welcome.svg │ │ ├── overview-welcome.svg │ │ ├── logo.svg │ │ ├── logo-symbol.svg │ │ ├── overview-welcome-yjs.svg │ │ ├── yjs-logo.svg │ │ └── logo-reverse.svg │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── opensans-300.eot │ │ ├── opensans-300.ttf │ │ ├── opensans-300.woff │ │ ├── opensans-300.woff2 │ │ ├── opensans-300i.eot │ │ ├── opensans-300i.ttf │ │ ├── opensans-300i.woff │ │ ├── opensans-400.eot │ │ ├── opensans-400.ttf │ │ ├── opensans-400.woff │ │ ├── opensans-400.woff2 │ │ ├── opensans-400i.eot │ │ ├── opensans-400i.ttf │ │ ├── opensans-400i.woff │ │ ├── opensans-600.eot │ │ ├── opensans-600.ttf │ │ ├── opensans-600.woff │ │ ├── opensans-600.woff2 │ │ ├── opensans-700.eot │ │ ├── opensans-700.ttf │ │ ├── opensans-700.woff │ │ ├── opensans-700.woff2 │ │ ├── cloudflare-font.eot │ │ ├── cloudflare-font.ttf │ │ ├── cloudflare-font.woff │ │ ├── opensans-300i.woff2 │ │ ├── opensans-400i.woff2 │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-cloudflare.eot │ │ ├── fontawesome-cloudflare.ttf │ │ ├── fontawesome-cloudflare.woff │ │ ├── fontawesome-cloudflare.svg │ │ └── cloudflare-font.svg │ ├── config.js │ ├── stylesheets │ │ └── hacks.css │ └── lang │ │ └── en.js │ ├── layout │ └── cloudflare_plugin_index.xml │ └── templates │ └── plugin │ └── index.phtml ├── registration.php ├── etc ├── module.xml ├── adminhtml │ ├── routes.xml │ └── menu.xml ├── frontend │ └── di.xml ├── events.xml └── di.xml ├── Model ├── KeyValue.php ├── ResourceModel │ └── KeyValue.php └── Layout │ └── LayoutPlugin.php ├── Backend ├── ClientRoutes.php ├── PluginRoutes.php ├── PluginAPI.php ├── MagentoConfig.php ├── MagentoIntegration.php ├── ClientActions.php ├── ClientAPI.php ├── MagentoHttpClient.php ├── DataStore.php ├── CacheTags.php ├── MagentoAPI.php └── PluginActions.php ├── Test ├── Unit │ ├── Backend │ │ ├── PluginRoutesTest.php │ │ ├── DataStoreTest.php │ │ ├── PluginActionsTest.php │ │ ├── ClientActionsTest.php │ │ ├── CacheTagsTest.php │ │ └── MagentoAPITest.php │ ├── Observer │ │ ├── FlushAllCloudFlareObserverTest.php │ │ └── InvalidateCloudFlareObserverTest.php │ ├── Model │ │ └── Layout │ │ │ └── LayoutPluginTest.php │ └── Controller │ │ └── Adminhtml │ │ └── Plugin │ │ └── ProxyTest.php └── bootstrap.php ├── .github └── workflows │ └── semgrep.yml ├── CHANGELOG.md ├── Observer ├── FlushAllCloudFlareObserver.php └── InvalidateCloudFlareObserver.php ├── composer.json ├── LICENSE.md ├── README.md ├── .travis.yml ├── Controller └── Adminhtml │ └── Plugin │ ├── Index.php │ └── Proxy.php ├── Block └── Adminhtml │ └── Index.php ├── Setup └── InstallSchema.php └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | auth.json 2 | sync.sh 3 | vendor/ 4 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/js/.DS_Store -------------------------------------------------------------------------------- /view/adminhtml/web/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/.DS_Store -------------------------------------------------------------------------------- /view/adminhtml/web/assets/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/layers.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/spinner.gif -------------------------------------------------------------------------------- /view/adminhtml/web/assets/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/throbber.gif -------------------------------------------------------------------------------- /view/adminhtml/web/assets/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/layers-2x.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/select2-cf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/select2-cf.png -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /view/adminhtml/web/assets/select2x2-cf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/select2x2-cf.png -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300i.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300i.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300i.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300i.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300i.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400i.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400i.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400i.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400i.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400i.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400i.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-600.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-600.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-600.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-600.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-700.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-700.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-700.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-700.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/assets/details-arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/details-arrows.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/hero-bg-clouds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/hero-bg-clouds.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icon-bolt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/vertical-range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/vertical-range.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/yjs-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/yjs-background.jpg -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/cloudflare-font.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/cloudflare-font.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/cloudflare-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/cloudflare-font.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/cloudflare-font.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/cloudflare-font.woff -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-300i.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-300i.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/opensans-400i.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/opensans-400i.woff2 -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icons-seee324dde5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/icons-seee324dde5.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/select2-cf-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/select2-cf-white.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/select2x2-cf-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/select2x2-cf-white.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/yjs-background_2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/yjs-background_2x.jpg -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icons_2x-s6333fe7591.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/icons_2x-s6333fe7591.png -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /view/adminhtml/web/assets/modal-two-factor-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/modal-two-factor-auth.png -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-cloudflare.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-cloudflare.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-cloudflare.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-cloudflare.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-cloudflare.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/fonts/fontawesome-cloudflare.woff -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icon-shield.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/modal-two-factor-auth_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/Cloudflare-Magento/master/view/adminhtml/web/assets/modal-two-factor-auth_2x.png -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icon-lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/icon-pin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Model/KeyValue.php: -------------------------------------------------------------------------------- 1 | _init('CloudFlare\Plugin\Model\ResourceModel\KeyValue'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /etc/frontend/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Backend/ClientRoutes.php: -------------------------------------------------------------------------------- 1 | array( 9 | 'class' => 'CloudFlare\Plugin\Backend\ClientActions', 10 | 'methods' => array( 11 | 'GET' => array( 12 | 'function' => 'getZonesReturnMagentoZone' 13 | ) 14 | ) 15 | ) 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /Model/ResourceModel/KeyValue.php: -------------------------------------------------------------------------------- 1 | _init(InstallSchema::CLOUDFLARE_DATA_TABLE_NAME, InstallSchema::CLOUDFLARE_DATA_TABLE_ID_COLUMN); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Backend/PluginRoutes.php: -------------------------------------------------------------------------------- 1 | $route) { 15 | $route['class'] = '\CloudFlare\Plugin\Backend\PluginActions'; 16 | $routeList[$routePath] = $route; 17 | } 18 | 19 | return $routeList; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Backend/PluginAPI.php: -------------------------------------------------------------------------------- 1 | magentoHttpClient = $magentoHttpClient; 17 | $this->magentoHttpClient->setEndpoint($this->getEndpoint()); 18 | $this->setHttpClient($this->magentoHttpClient); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Test/Unit/Backend/PluginRoutesTest.php: -------------------------------------------------------------------------------- 1 | array( 13 | 'class' => 'Any/Other/Class' 14 | ) 15 | ); 16 | 17 | $newRoutes = PluginRoutes::getRoutes($routes); 18 | 19 | $this->assertEquals('\CloudFlare\Plugin\Backend\PluginActions', $newRoutes['account']['class']); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Test/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Backend/MagentoConfig.php: -------------------------------------------------------------------------------- 1 | config = []; 17 | } 18 | 19 | /** 20 | * @param Array $config 21 | */ 22 | public function setConfig($config) 23 | { 24 | $this->config = $config; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [1.1.8](#1.1.8) - 2018-07-23 6 | ## Fixed 7 | - Magento 2 js minification bug [#72](https://github.com/cloudflare/Cloudflare-Magento/pull/72) 8 | 9 | ## [1.1.7](#1.1.7) - 2017-11-29 10 | ## Added 11 | - Support for PHP version 7.x. [#68](https://github.com/cloudflare/Cloudflare-Magento/pull/68) 12 | 13 | ## [1.1.6](#1.1.6) - 2017-05-24 14 | ## Fixed 15 | - Fixed bug where DB errors weren't being caught. [#59](https://github.com/cloudflare/Cloudflare-Magento/pull/59) 16 | 17 | ## [1.1.5](#1.1.5) - 2017-04-21 18 | ## Fixed 19 | - Fixed bug where Magento 2.1 DI couldn't inject PHP classes from non Magento module composer classes [#55](https://github.com/cloudflare/Cloudflare-Magento/pull/55) 20 | -------------------------------------------------------------------------------- /view/adminhtml/web/config.js: -------------------------------------------------------------------------------- 1 | { 2 | "debug": false, 3 | "featureManagerIsFullZoneProvisioningEnabled": false, 4 | "isDNSPageEnabled": false, 5 | "isSubdomainCheckEnabled": false, 6 | "homePageCards": [ 7 | "ApplyDefaultSettingsCard", 8 | "PluginSpecificCacheTagCard", 9 | "PurgeCacheCard" 10 | ], 11 | "moreSettingsCards": { 12 | "container.moresettings.security": [ 13 | "SecurityLevelCard", 14 | "WAFCard" 15 | ], 16 | "container.moresettings.performance": [ 17 | "BypassCacheByCookieCard", 18 | "AlwaysOnlineCard", 19 | "ImageOptimizationCard", 20 | "DevelopmentModeCard" 21 | ] 22 | }, 23 | "locale": "en", 24 | "integrationName": "Magento", 25 | "useHostAPILogin": false, 26 | "version": "1.1.8" 27 | } 28 | -------------------------------------------------------------------------------- /Observer/FlushAllCloudFlareObserver.php: -------------------------------------------------------------------------------- 1 | config = $config; 34 | $this->cacheTags = $cacheTags; 35 | } 36 | 37 | /** 38 | * Flush CloudFlare cache 39 | * 40 | * @param \Magento\Framework\Event\Observer $observer 41 | * @return void 42 | */ 43 | public function execute(Observer $observer) 44 | { 45 | if ($this->config->isEnabled()) { 46 | $this->cacheTags->purgeCache(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare/cloudflare-magento", 3 | "description": "Cloudflare Plugin for Magento2", 4 | "version": "1.1.8", 5 | "homepage": "https://github.com/cloudflare/Cloudflare-Magento", 6 | "scripts": { 7 | "test": "vendor/phpunit/phpunit/phpunit --bootstrap Test/bootstrap.php Test/ && rm -rf phpUnitGeneratedFiles/", 8 | "lint": "vendor/bin/phpcs -n --standard=PSR2 Backend/ Block/ Controller/ Model/ Observer/ Setup/ Test/", 9 | "format": "vendor/bin/phpcbf --standard=PSR2 Backend/ Block/ Controller/ Model/ Observer/ Setup/ Test/" 10 | }, 11 | "repositories": [ 12 | { 13 | "type": "composer", 14 | "url": "https://repo.magento.com/" 15 | } 16 | ], 17 | "require": { 18 | "php": "~5.6.0|~7.0", 19 | "cloudflare/cloudflare-plugin-backend": "^2.1" 20 | }, 21 | "type": "magento2-module", 22 | "license": [ 23 | "BSD-3-Clause" 24 | ], 25 | "autoload": { 26 | "files": [ "registration.php" ], 27 | "psr-4": { 28 | "CloudFlare\\Plugin\\": "" 29 | } 30 | }, 31 | "minimum-stability": "alpha", 32 | "require-dev": { 33 | "squizlabs/php_codesniffer": "^2.8", 34 | "phpunit/phpunit": "4.1.0", 35 | "magento/module-backend": "100.1.*", 36 | "magento/module-store": "100.1.*", 37 | "magento/framework": "100.1.*" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Test/Unit/Observer/FlushAllCloudFlareObserverTest.php: -------------------------------------------------------------------------------- 1 | mockCacheTags = $this->getMockBuilder('\CloudFlare\Plugin\Backend\CacheTags') 17 | ->disableOriginalConstructor() 18 | ->getMock(); 19 | $this->mockConfig = $this->getMockBuilder('\Magento\PageCache\Model\Config') 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | $this->mockObserver = $this->getMockBuilder('\Magento\Framework\Event\Observer') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->flushAllCloudFlareObserver = new FlushAllCloudFlareObserver($this->mockConfig, $this->mockCacheTags); 26 | } 27 | 28 | public function testExecuteCallsPurgeCache() 29 | { 30 | $this->mockConfig->method('isEnabled')->willReturn(true); 31 | $this->mockCacheTags->expects($this->once())->method('purgeCache'); 32 | 33 | $this->flushAllCloudFlareObserver->execute($this->mockObserver); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Cloudflare. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Backend/MagentoIntegration.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 26 | parent::__construct($config, $integrationAPI, $dataStore, $logger); 27 | } 28 | 29 | /** 30 | * @return HttpClientInterface $httpClient 31 | */ 32 | public function getHttpClient() 33 | { 34 | return $this->httpClient; 35 | } 36 | 37 | /** 38 | * @param HttpClientInterface $httpClient 39 | */ 40 | public function setHttpClient(HttpClientInterface $httpClient) 41 | { 42 | $this->httpClient = $httpClient; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notice: This plugin has been deprecated and is no longer officially supported by Cloudflare. 2 | ## For information regarding using Magento & Cloudflare, please see [this support article](https://support.cloudflare.com/hc/en-us/articles/360021023712-Best-Practices-Speed-up-your-Site-with-Custom-Caching-via-Cloudflare-Page-Rules). 3 | 4 | [![Build Status](https://travis-ci.org/cloudflare/Cloudflare-Magento.svg?branch=master)](https://travis-ci.org/cloudflare/Cloudflare-Magento) 5 | 6 | ## Installing the Cloudflare Magento2 extension 7 | From the magento2 root directory run the following commands: 8 | 9 | 1. `composer require cloudflare/cloudflare-magento` 10 | 2. `composer update` 11 | 3. `bin/magento setup:upgrade` 12 | 4. `bin/magento setup:di:compile` 13 | 14 | ## Versions of Magento2 supported 15 | * Up to Magento2 CE 2.2.0 16 | 17 | ## Development 18 | You'll need to get [authorization keys](http://devdocs.magento.com/guides/v2.0/install-gde/prereq/connect-auth.html) from the Magento marketplace and make an `auth.json`: 19 | ``` 20 | { 21 | "http-basic": { 22 | "repo.magento.com": { 23 | "username": "[MAGENTO USERNAME]", 24 | "password": "[MAGENTO PASSWORD]" 25 | } 26 | } 27 | } 28 | ``` 29 | This will allow `composer install` to authenticate against `repo.magento.com/`. 30 | 31 | ### Development Commands 32 | 1. `composer test` 33 | 2. `composer lint` 34 | 3. `composer format` 35 | 36 | ## Tests 37 | `vendor/phpunit/phpunit/phpunit -c dev/tests/unit/phpunit.xml.dist vendor/cloudflare/cloudflare-magento/Test/Unit/` 38 | -------------------------------------------------------------------------------- /Observer/InvalidateCloudFlareObserver.php: -------------------------------------------------------------------------------- 1 | config = $config; 33 | $this->cacheTags = $cacheTags; 34 | } 35 | 36 | /** 37 | * Purges CloudFlare cache by tag 38 | * 39 | * @param \Magento\Framework\Event\Observer $observer 40 | * @return void 41 | */ 42 | public function execute(\Magento\Framework\Event\Observer $observer) 43 | { 44 | if (!$this->config->isEnabled()) { 45 | return; 46 | } 47 | $object = $observer->getEvent()->getObject(); 48 | 49 | if ($object instanceof \Magento\Framework\DataObject\IdentityInterface === false 50 | || empty($object->getIdentities())) { 51 | return; 52 | } 53 | 54 | $this->cacheTags->purgeCacheTags($object->getIdentities()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | php: 4 | - 5.6 5 | - 7.0 6 | install: 7 | - echo "{\"http-basic\":{\"repo.magento.com\":{\"username\":\"${MAGENTO_USERNAME}\",\"password\":\"${MAGENTO_PASSWORD}\"}}}" > auth.json 8 | - composer install 9 | script: 10 | - composer lint 11 | - composer test 12 | env: 13 | global: 14 | - secure: "0Nl50VCOPwGTp54r1wbM0uFdBi7Do9Y0c2DyCizW2wGFW3UR06s0ps/ZSbwECXjTssL8yQV2I0YGLfnH7KcTKDdUH7IcMcn8RBjv+pJcnxSbgFI9EaGb2/gRjys687607Dtr/annx3fzKo2ao0ndxOdTRqEUU/FhKPM9pRkY+sTzePJfzEH6mnAcjK9r849nguaG/6wkiUlMG0WfAZ8x9z7DFUnQU6Iv+xpQfZO+qn5Da02gk/CNHlLoIQLvrNQ+Tfzki+F1moQQXslKSvq2noxo4SNdkUEgVPPq7x1IVXk56v0ovadLEF/MahB6wWuxcmD6pTh2r02sTJYS0lHY7LMnvGD9buq/hIo5Td2PAEVmmMBy2GUFtbBRp8kUv1dXgR7bm3ICiraufddftgYkcwLYdfP+q7PTdL5bzRodXaw97O48wwiKs7X1AjxO0tpzg3J+btSKuKS/wJmXtR188iven8BY4nq0+uFlz4U7aYEG3UG+nBqbDIF+amAkhPB9xlDzvrCYHsE9KRhX6b5zNAzlOJg71OpBSCNvxWwp99hnHz5km2Fx0NVYLf2NV7c5/fnw4JfWt7MWKzUPMq8fQME7B7K9lrw7mZnSWg7i0QRetyQ8rlcwNif7aq/XGSzy7ABmN2mrHgTbfeKVqvT8o3RSoS9bjt6gPjuq+Rrwzhs=" 15 | - secure: "6tcytWunexBbAeEAmpPgKPFW6dlzV/soHtuOiomS4a0p75rDrnlnc3BiLuFGFw2tp1MNrKdtwCSr+gvE/uu59afhY/a5HmOZwxhS8qUY6+RDEbVIolgaIDxhgdyAUBum/waz01r4orhGAtyVvG/ABVkKMqex6+bVGzzyRq+hpOhlboVrLVnFvVeCSQjlmsgLC55aqGcdOT0lRgi33EUKED4GXg7zLANeRfmOmFv7JC3sqEgEsUaiz4JoWQrH43LtDWYegJKAWgLS8joErnlsuSVCYAOxIPGFT9Dhiqz6P3wV+CXxDaUprln98Kk/bUGHa7TaURSHVEBYScKoElaL44nEJQQWPY27PeUShlJtYPmhUCcV1HhzdVpb3rytnCmKWBLnvLfxhM2EKMpSB+fxI8/NMXGKRnsHt9vRmSOqYDhKGDf8UAtTPs6BYmGHzbRqO75dbCmxPuLRTzDm69YiUjmWdQJemS1FwR2wPbccyk0NtF3Jd0BLdozCypiuRpuvvlc5fPDddUJC16C9wUjRilJJINIekYypk5pm5Lp/SDnqcm+P2aQLmEU1uRjeaD55w524tlYgI5F2Aqh24BHM65EEBgdZALiyv8OtlE3eT9DxAFoVboH5qnE3EE6Lp2i9lovLGCaQfxzx3fJfd11xFhkIgWtjko5QcGHKngYh13E=" 16 | -------------------------------------------------------------------------------- /view/adminhtml/templates/plugin/index.phtml: -------------------------------------------------------------------------------- 1 | 40 | 41 |
42 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Plugin/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 39 | $this->scopeConfig = $scopeConfig; // Needed to retrieve config values 40 | $this->logger = $logger; 41 | } 42 | 43 | /** 44 | * Index Action* 45 | * @return void 46 | */ 47 | public function execute() 48 | { 49 | /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ 50 | $resultPage = $this->resultPageFactory->create(); 51 | $resultPage->setActiveMenu('CloudFlare_Plugin::index'); 52 | $resultPage->addBreadcrumb(__('System'), __('System')); 53 | $resultPage->addBreadcrumb(__('CloudFlare'), __('Cloudflare')); 54 | $resultPage->getConfig()->getTitle()->prepend(__('Cloudflare')); 55 | return $resultPage; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Test/Unit/Model/Layout/LayoutPluginTest.php: -------------------------------------------------------------------------------- 1 | mockCacheTags = $this->getMockBuilder('\CloudFlare\Plugin\Backend\CacheTags') 17 | ->disableOriginalConstructor() 18 | ->getMock(); 19 | $this->mockConfig = $this->getMockBuilder('\Magento\PageCache\Model\Config') 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->mockResponse = $this->getMockBuilder('\Magento\Framework\App\ResponseInterface') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | $this->layoutPlugin = new LayoutPlugin( 29 | $this->mockResponse, 30 | $this->mockConfig, 31 | $this->mockLogger, 32 | $this->mockCacheTags 33 | ); 34 | } 35 | 36 | public function testLayoutPluginCallsSetCloudFlareCacheTagsResponseHeaderOnce() 37 | { 38 | $mockSubject = $this->getMockBuilder('\Magento\Framework\View\Layout') 39 | ->disableOriginalConstructor() 40 | ->getMock(); 41 | $mockSubject->method('isCacheable')->willReturn(true); 42 | $mockSubject->method('getAllBlocks')->willReturn(array()); 43 | $this->mockConfig->method('isEnabled')->willReturn(true); 44 | $this->mockCacheTags->expects($this->once()) 45 | ->method('setCloudFlareCacheTagsResponseHeader'); 46 | 47 | $this->layoutPlugin->afterGetOutput($mockSubject, null); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Test/Unit/Observer/InvalidateCloudFlareObserverTest.php: -------------------------------------------------------------------------------- 1 | mockCacheTags = $this->getMockBuilder('\CloudFlare\Plugin\Backend\CacheTags') 19 | ->disableOriginalConstructor() 20 | ->getMock(); 21 | $this->mockConfig = $this->getMockBuilder('\Magento\PageCache\Model\Config') 22 | ->disableOriginalConstructor() 23 | ->getMock(); 24 | 25 | $this->mockEvent = $this->getMock('Magento\Framework\Event', ['getObject'], [], '', false); 26 | 27 | $this->mockObject = $this->getMockBuilder('\Magento\Framework\DataObject\IdentityInterface') 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | $this->mockObserver = $this->getMockBuilder('\Magento\Framework\Event\Observer') 31 | ->disableOriginalConstructor() 32 | ->getMock(); 33 | $this->invalidateCloudFlareObserver = new InvalidateCloudFlareObserver($this->mockConfig, $this->mockCacheTags); 34 | } 35 | 36 | public function testExecuteCallsPurgeCacheTags() 37 | { 38 | $this->mockConfig->method('isEnabled')->willReturn(true); 39 | $this->mockObject->method('getIdentities')->willReturn(array('cacheTagToPurge')); 40 | $this->mockEvent->method('getObject')->willReturn($this->mockObject); 41 | $this->mockObserver->method('getEvent')->willReturn($this->mockEvent); 42 | $this->mockCacheTags->expects($this->once())->method('purgeCacheTags'); 43 | 44 | $this->invalidateCloudFlareObserver->execute($this->mockObserver); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Block/Adminhtml/Index.php: -------------------------------------------------------------------------------- 1 | assetRepository = $context->getAssetRepository(); 32 | $this->dataStore = $dataStore; 33 | $this->magentoAPI = $magentoAPI; 34 | $this->logger = $context->getLogger(); 35 | $this->urlBuilder = $urlBuilder; 36 | 37 | parent::__construct($context); 38 | } 39 | 40 | /* 41 | * $this->set*() are "magic" in that you can call set[THING]() and magento store and expose a 42 | * get[THING]() for you to retrieve the value on the front end. 43 | */ 44 | protected function _prepareLayout() 45 | { 46 | //Generate link to CloudFlare/Plugin/view/web/js/compiled.min.js 47 | $asset = $this->assetRepository->createAsset('CloudFlare_Plugin::js/compiled.min.js'); 48 | $compiledJsUrl = $asset->getUrl(); 49 | $this->setCompiledJsUrl($compiledJsUrl); 50 | 51 | $restProxyPrefix = str_replace(self::COMPILED_JS_PATH, "", $compiledJsUrl); 52 | $this->setRestProxyPrefix($restProxyPrefix); 53 | 54 | $this->setProxyUrl($this->urlBuilder->getUrl("cloudflare/plugin/proxy")); 55 | $this->setCloudflareEmail($this->dataStore->getCloudFlareEmail()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Model/Layout/LayoutPlugin.php: -------------------------------------------------------------------------------- 1 | response = $response; 48 | $this->config = $config; 49 | $this->logger = $logger; 50 | $this->cacheTagsUtil = $cacheTagsUtil; 51 | } 52 | 53 | 54 | /** 55 | * Set X-Cache-Tags header with all the Magento Cache Tags so 56 | * they can be purged by the CloudFlare API 57 | * 58 | * @param \Magento\Framework\View\Layout $subject 59 | * @param $result 60 | * @return mixed 61 | */ 62 | public function afterGetOutput(\Magento\Framework\View\Layout $subject, $result) 63 | { 64 | if (!$subject->isCacheable() || !$this->config->isEnabled()) { 65 | return $result; 66 | } 67 | 68 | $tags = []; 69 | foreach ($subject->getAllBlocks() as $block) { 70 | if ($block->getIdentities() !== null) { 71 | $tags = array_merge($tags, $block->getIdentities()); 72 | } 73 | } 74 | $tags = array_unique($tags); 75 | 76 | $this->cacheTagsUtil->setCloudFlareCacheTagsResponseHeader($this->response, $tags); 77 | 78 | return $result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Setup/InstallSchema.php: -------------------------------------------------------------------------------- 1 | startSetup(); 21 | 22 | $tableName = $installer->getTable(self::CLOUDFLARE_DATA_TABLE_NAME); 23 | 24 | if ($installer->getConnection()->isTableExists($tableName) != true) { 25 | $table = $installer->getConnection() 26 | ->newTable($tableName) 27 | ->addColumn( 28 | self::CLOUDFLARE_DATA_TABLE_ID_COLUMN, 29 | Table::TYPE_INTEGER, 30 | null, 31 | [ 32 | 'identity' => true, 33 | 'unsigned' => true, 34 | 'nullable' => false, 35 | 'primary' => true 36 | ], 37 | 'ID' 38 | ) 39 | ->addColumn( 40 | self::CLOUDFLARE_DATA_TABLE_KEY_COLUMN, 41 | Table::TYPE_TEXT, 42 | null, 43 | ['nullable' => false, 'default' => ''], 44 | 'Key' 45 | ) 46 | ->addColumn( 47 | self::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, 48 | Table::TYPE_TEXT, 49 | null, 50 | ['nullable' => true, 'default' => ''], 51 | 'Value' 52 | ) 53 | ->setComment('CloudFlare Key/Value Store.') 54 | ->setOption('type', 'InnoDB') 55 | ->setOption('charset', 'utf8'); 56 | $installer->getConnection()->createTable($table); 57 | } 58 | 59 | $installer->endSetup(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Backend/ClientActions.php: -------------------------------------------------------------------------------- 1 | api = $api; 25 | $this->config = $magentoIntegration->getConfig(); 26 | $this->integrationAPI = $magentoIntegration->getIntegrationAPI(); 27 | $this->dataStore = $magentoIntegration->getDataStore(); 28 | $this->logger = $magentoIntegration->getLogger(); 29 | $this->request = $request; 30 | } 31 | 32 | /* 33 | * GET /zones 34 | * 35 | * To ensure the plugin can only be used to manage the current Magento installation we 36 | * hook on this call to only return a list of size one of the current domain. 37 | */ 38 | public function getZonesReturnMagentoZone() 39 | { 40 | $magentoDomainName = $this->integrationAPI->getMagentoDomainName(); 41 | 42 | $response = $this->api->callAPI($this->request); 43 | if ($this->api->responseOk($response)) { 44 | $magentoZone = null; 45 | $bestMatch = strlen($magentoDomainName); 46 | foreach ($response['result'] as $zone) { 47 | $firstOccurrence = strpos($magentoDomainName, $zone['name']); 48 | if ($firstOccurrence !== false && $firstOccurrence < $bestMatch) { 49 | $bestMatch = $firstOccurrence; 50 | $magentoZone = $zone; 51 | } 52 | } 53 | if ($magentoZone === null) { 54 | $this->logger->warning($magentoDomainName . 'doesn\'t appear to be provisioned on CloudFlare.com.'); 55 | $magentoZone = array( 56 | 'name' => $magentoDomainName, 57 | 'plan' => array('name' => ''), 58 | 'type' => '', 59 | 'status' => 'inactive', 60 | ); 61 | } 62 | $response['result'] = array($magentoZone); 63 | } 64 | 65 | return $response; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Backend/ClientAPI.php: -------------------------------------------------------------------------------- 1 | getHttpClient(); 17 | $httpClient->setEndpoint($this->getEndpoint()); 18 | $this->setHttpClient($httpClient); 19 | 20 | parent::__construct($integration); 21 | } 22 | 23 | /** 24 | * DELETE zones/:id/purge_cache 25 | * https://api.cloudflare.com/#zone-purge-individual-files-by-url-and-cache-tags 26 | * 27 | * @param String[] $tags 28 | * @return 29 | */ 30 | public function zonePurgeCacheByTags($tags) 31 | { 32 | $zoneId = $this->getZoneIdForDomainName($this->integrationAPI->getMagentoDomainName()); 33 | 34 | if ($zoneId) { 35 | $request = new Request('DELETE', 'zones/'. $zoneId .'/purge_cache', array(), array('tags' => $tags)); 36 | 37 | return $this->callAPI($request); 38 | } 39 | } 40 | 41 | /** 42 | * DELETE zones/:id/purge_cache 43 | * https://api.cloudflare.com/#zone-purge-all-files 44 | * 45 | * @return mixed 46 | */ 47 | public function zonePurgeCache() 48 | { 49 | $zoneId = $this->getZoneIdForDomainName($this->integrationAPI->getMagentoDomainName()); 50 | if ($zoneId) { 51 | $request = new Request('DELETE', 'zones/'. $zoneId .'/purge_cache', array(), array('purge_everything' => true)); 52 | 53 | return $this->callAPI($request); 54 | } 55 | } 56 | 57 | /** 58 | * @param String $domainName 59 | * @return null 60 | */ 61 | protected function getZoneIdForDomainName($domainName) 62 | { 63 | $zoneId = $this->data_store->getZoneId($domainName); 64 | if ($zoneId !== null) { 65 | return $zoneId; 66 | } 67 | 68 | $request = new Request('GET', 'zones', array('name' => $domainName), array()); 69 | $response = $this->callAPI($request); 70 | try { 71 | if ($this->responseOk($response) && count($response['result']) > 0) { 72 | $zoneId = $response['result'][0]['id']; 73 | $this->data_store->setZoneId($domainName, $zoneId); 74 | return $zoneId; 75 | } 76 | } catch (\Exception $e) { 77 | $this->logger->error($e->getMessage()); 78 | } 79 | 80 | return null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Backend/MagentoHttpClient.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 20 | } 21 | 22 | public function send(Request $request) 23 | { 24 | $client = $this->createZendClient($request); 25 | try { 26 | $response = $client->request(); 27 | return json_decode($response->getBody(), true); 28 | } catch (\Zend_Http_Client_Exception $e) { 29 | $this->logAPICall($this->endpoint, array( 30 | 'type' => 'request', 31 | 'method' => $request->getMethod(), 32 | 'path' => $request->getUrl(), 33 | 'headers' => $request->getHeaders(), 34 | 'params' => $request->getParameters(), 35 | 'body' => $request->getBody()), true); 36 | $this->logAPICall($this->endpoint, array('type' => 'response', 'code' => $e->getCode(), 'body' => $e->getMessage(), 'stacktrace' => $e->getTraceAsString()), true); 37 | } 38 | 39 | return null; 40 | } 41 | 42 | /** 43 | * @param Request $request 44 | * @return ZendClient $client 45 | */ 46 | public function createZendClient(Request $request) 47 | { 48 | $client = new \Zend_Http_Client(); 49 | $client->setUri($this->endpoint . $request->getUrl()); 50 | 51 | $client->setMethod($request->getMethod()); 52 | 53 | $client->setHeaders($request->getHeaders()); 54 | $client->setParameterGet($request->getParameters()); 55 | 56 | if ($request->getMethod() !== 'GET') { 57 | $client->setRawData(json_encode($request->getBody()), 'application/json'); 58 | } 59 | 60 | return $client; 61 | } 62 | 63 | /** 64 | * @param string $apiName 65 | * @param array $message 66 | * @param bool $isError 67 | */ 68 | public function logAPICall($apiName, $message, $isError) 69 | { 70 | $logLevel = 'error'; 71 | if ($isError === false) { 72 | $logLevel = 'debug'; 73 | } 74 | if (!is_string($message)) { 75 | $message = print_r($message, true); 76 | } 77 | $this->logger->$logLevel('['.$apiName.'] '.$message); 78 | } 79 | 80 | public function setEndpoint($endpoint) 81 | { 82 | $this->endpoint = $endpoint; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Backend/DataStore.php: -------------------------------------------------------------------------------- 1 | magentoAPI = $magentoAPI; 17 | } 18 | 19 | /** 20 | * @param $clientAPIKey 21 | * @param $email 22 | * @param $uniqueId 23 | * @param $userKey 24 | * @return mixed 25 | * @internal param $client_api_key 26 | * @internal param $unique_id 27 | * @internal param $user_key 28 | */ 29 | public function createUserDataStore($clientAPIKey, $email, $uniqueId, $userKey) 30 | { 31 | //Magento doesn't use the host api - $uniqueId, $userKey will always be null 32 | $this->set(self::CLIENT_API_KEY, $clientAPIKey); 33 | $this->set(self::CLOUDFLARE_EMAIL, $email); 34 | return true; 35 | } 36 | 37 | /** 38 | * @return mixed 39 | */ 40 | public function getHostAPIUserUniqueId() 41 | { 42 | return null; 43 | } 44 | 45 | /** 46 | * @return mixed 47 | */ 48 | public function getClientV4APIKey() 49 | { 50 | return $this->get(self::CLIENT_API_KEY); 51 | } 52 | 53 | /** 54 | * @return mixed 55 | */ 56 | public function getHostAPIUserKey() 57 | { 58 | return null; 59 | } 60 | 61 | /** 62 | * @return mixed 63 | */ 64 | public function getCloudFlareEmail() 65 | { 66 | return $this->get(self::CLOUDFLARE_EMAIL); 67 | } 68 | 69 | /** 70 | * @param $domainName 71 | * @return null 72 | */ 73 | public function getZoneId($domainName) 74 | { 75 | return $this->get(self::ZONE_ID_KEY . $domainName); 76 | } 77 | 78 | /** 79 | * @param $domainName 80 | * @param $zoneId 81 | * @return mixed 82 | */ 83 | public function setZoneId($domainName, $zoneId) 84 | { 85 | return $this->set(self::ZONE_ID_KEY . $domainName, $zoneId); 86 | } 87 | 88 | /** 89 | * @param $key 90 | * @return mixed 91 | */ 92 | public function get($key) 93 | { 94 | return json_decode($this->magentoAPI->getValue($key), true); 95 | } 96 | 97 | /** 98 | * @param $key 99 | * @param $value 100 | * @return mixed 101 | */ 102 | public function set($key, $value) 103 | { 104 | return $this->magentoAPI->setValue($key, json_encode($value)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/request-submitted-success.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Cloudflare Plugins 2 | 3 | 👍🎉 First off, thanks for taking the time to contribute! 🎉👍 4 | 5 | ## How To Contribute 6 | 7 | We welcome community contribution to this repository. To help add functionality or address issues, please take the following steps: 8 | 9 | * Fork the repository from the master branch. 10 | * Create a new branch for your features / fixes. 11 | * Make the changes you wish to see. 12 | * Add tests for all changes. 13 | * Create a pull request with details of what changes have been made, explanation of new behaviour, and link to issue that is addressed. 14 | * Addressing (with @...) one or more of the maintainers in the description of the pull request 15 | * Ensure documentation contains the correct information. 16 | * Pull requests will be reviewed and hopefully merged into a release. 17 | 18 | ## Before Contributing 19 | 20 | Cloudflare has multiple plugins using shared codebases. 21 | 22 | [WordPress](https://github.com/cloudflare/Cloudflare-WordPress), [CPanel](https://github.com/cloudflare/CloudFlare-CPanel), [Magento](https://github.com/cloudflare/CloudFlare-Magento) are the main repositories of the plugins. Every plugin has a config.js file which allows them to control the frontend of the plugin. 23 | 24 | Below are Cloudflare maintained repositories the plugins depend on. 25 | 26 | * [cloudflare-frontend](https://github.com/cloudflare/CloudFlare-FrontEnd) is a generic frontend used in plugins. You can add/remove cards simply by editing [config](https://github.com/cloudflare/CloudFlare-FrontEnd/blob/master/config.js) file. 27 | * [cf-ui](https://github.com/cloudflare/cf-ui) is a Cloudflare UI Framework where cloudflare-frontend is using. 28 | * [cloudflare-plugin-backend](https://github.com/cloudflare/cloudflare-plugin-backend) is a generic backend plugins use. 29 | * [cf-ip-rewrite](https://github.com/cloudflare/cf-ip-rewrite) allows to rewrite Cloudflare IP's in Application level. 30 | * [mod_cloudflare](https://github.com/cloudflare/mod_cloudflare) allows Apache to rewrite Cloudflare IP's with user IP's. It is not used in plugins itself but it maybe be a better alternative then `cf-ip-rewrite`. 31 | 32 | ## Frontend Updates 33 | 34 | Each plugin may use different Frontend [versions]((https://github.com/cloudflare/CloudFlare-FrontEnd/releases)). When publishing a Frontend release we copy the following files to other plugins; 35 | 36 | * `assets/` 37 | * `fonts/` 38 | * `lang/` 39 | * `stylesheets/` 40 | * `compiled.min.js` which is created when `gulp compress` command is called within Frontend repository. 41 | 42 | ## Translations 43 | 44 | The plugins use a common language file which is located [here](https://github.com/cloudflare/CloudFlare-FrontEnd/tree/master/lang). English translation is always up to date where as other translations are not. If you have any issues or questions regarding with translations feel free to open an [issue](https://github.com/cloudflare/CloudFlare-FrontEnd/issues). 45 | -------------------------------------------------------------------------------- /etc/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/plan-changed-success.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/analytics-welcome.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | \Magento\Framework\App\DeploymentConfig\Reader 6 | \CloudFlare\Plugin\Model\KeyValueFactory 7 | \Magento\Store\Model\StoreManagerInterface 8 | \Psr\Log\LoggerInterface 9 | 10 | 11 | 12 | 13 | 14 | \CloudFlare\Plugin\Backend\MagentoAPI 15 | 16 | 17 | 18 | 19 | \Psr\Log\LoggerInterface 20 | 21 | 22 | 23 | 24 | 25 | \CloudFlare\Plugin\Backend\MagentoConfig 26 | \CloudFlare\Plugin\Backend\MagentoAPI 27 | \CloudFlare\Plugin\Backend\DataStore 28 | \Psr\Log\LoggerInterface 29 | \CloudFlare\Plugin\Backend\MagentoHttpClient 30 | 31 | 32 | 33 | 34 | 50 | 51 | 52 | 53 | 54 | HTTP_CF_CONNECTING_IP 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Test/Unit/Backend/DataStoreTest.php: -------------------------------------------------------------------------------- 1 | mockMagentoAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoAPI') 15 | ->disableOriginalConstructor() 16 | ->getMock(); 17 | $this->dataStore = new DataStore($this->mockMagentoAPI); 18 | } 19 | 20 | public function testCreateUserDataStoreSavesAPIKeyAndEmail() 21 | { 22 | $apiKey = "apiKey"; 23 | $email = "email"; 24 | 25 | $this->mockMagentoAPI->expects($this->at(0)) 26 | ->method('setValue') 27 | ->with(DataStore::CLIENT_API_KEY, json_encode($apiKey)); 28 | 29 | $this->mockMagentoAPI->expects($this->at(1)) 30 | ->method('setValue') 31 | ->with(DataStore::CLOUDFLARE_EMAIL, json_encode($email)); 32 | 33 | $this->dataStore->createUserDataStore($apiKey, $email, null, null); 34 | } 35 | 36 | public function testGetClientV4APIKeyReturnsCorrectValue() 37 | { 38 | $apiKey = "apiKey"; 39 | $this->mockMagentoAPI->method('getValue')->willReturn(json_encode($apiKey)); 40 | 41 | $response = $this->dataStore->getClientV4APIKey(); 42 | $this->assertEquals($response, $apiKey); 43 | } 44 | 45 | public function testGetCloudFlareEmailReturnsCorrectValue() 46 | { 47 | $email = "email"; 48 | $this->mockMagentoAPI->method('getValue')->willReturn(json_encode($email)); 49 | 50 | $response = $this->dataStore->getClientV4APIKey(); 51 | $this->assertEquals($response, $email); 52 | } 53 | 54 | public function testGetHostAPIUserKeyReturnsNull() 55 | { 56 | $this->assertNull($this->dataStore->getHostAPIUserKey()); 57 | } 58 | 59 | public function testGetHostAPIUserUniqueIdReturnsNull() 60 | { 61 | $this->assertNull($this->dataStore->getHostAPIUserUniqueId()); 62 | } 63 | 64 | public function testGetZoneIdReturnsValue() 65 | { 66 | $domain = "domain"; 67 | $key = DataStore::ZONE_ID_KEY.$domain; 68 | $zoneId = "zoneId"; 69 | 70 | $this->mockMagentoAPI->method('getValue')->with($key)->willReturn(json_encode($zoneId)); 71 | $response = $this->dataStore->getZoneId($domain); 72 | $this->assertEquals($zoneId, $response); 73 | } 74 | 75 | public function testSetZoneIdSetsValue() 76 | { 77 | $domain = "domain"; 78 | $key = DataStore::ZONE_ID_KEY.$domain; 79 | $zoneId = "zoneId"; 80 | 81 | $this->mockMagentoAPI->expects($this->once())->method('setValue')->with($key, json_encode($zoneId)); 82 | $this->dataStore->setZoneId($domain, $zoneId); 83 | } 84 | 85 | public function testGetCallsMagentoAPIGetValue() 86 | { 87 | $key = "key"; 88 | $this->mockMagentoAPI->expects($this->once())->method('getValue')->with($key); 89 | $this->dataStore->get($key); 90 | } 91 | 92 | public function testSetCallsMagentoAPISetValue() 93 | { 94 | $key = "key"; 95 | $value = "value"; 96 | $this->mockMagentoAPI->expects($this->once())->method('setValue')->with($key, json_encode($value)); 97 | $this->dataStore->set($key, $value); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Test/Unit/Backend/PluginActionsTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('\CF\Integration\DefaultConfig') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | $this->mockClientAPIClient = $this->getMockBuilder('\CF\API\Client') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockDataStore = $this->getMockBuilder('\CloudFlare\Plugin\Backend\DataStore') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockMagentoAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoAPI') 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | $this->mockPluginAPIClient = $this->getMockBuilder('\CF\API\Client') 41 | ->disableOriginalConstructor() 42 | ->getMock(); 43 | $this->mockRequest = $this->getMockBuilder('\CF\API\Request') 44 | ->disableOriginalConstructor() 45 | ->getMock(); 46 | $this->mockHttpClientInterface = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoHttpClient') 47 | ->disableOriginalConstructor() 48 | ->getMock(); 49 | 50 | $this->mockIntegrationContext = new MagentoIntegration( 51 | $this->mockConfig, 52 | $this->mockMagentoAPI, 53 | $this->mockDataStore, 54 | $this->mockLogger, 55 | $this->mockHttpClientInterface 56 | ); 57 | $this->pluginActions = new PluginActions( 58 | $this->mockIntegrationContext, 59 | $this->mockPluginAPIClient, 60 | $this->mockRequest 61 | ); 62 | $this->pluginActions->setClientAPI($this->mockClientAPIClient); 63 | } 64 | 65 | public function testPatchZoneSettingThrowsExceptionForBadResponse() 66 | { 67 | $this->setExpectedException(ZoneSettingFailException::class); 68 | $this->mockClientAPIClient->method('callAPI')->willReturn(array('errors' => array())); 69 | $this->pluginActions->patchZoneSetting(null, null, null); 70 | } 71 | 72 | public function testPatchZoneSettingWillReturnTrueForPlanUpgradeError() 73 | { 74 | $this->mockClientAPIClient->method('callAPI')->willReturn(array( 75 | 'errors' => array( 76 | array( 77 | 'code' => '', 78 | 'message' => 'Not allowed to edit setting for polish' 79 | ) 80 | ) 81 | )); 82 | $this->assertTrue($this->pluginActions->patchZoneSetting(null, null, null)); 83 | } 84 | 85 | public function testPostPageRuleThrowsExceptionForBadResponse() 86 | { 87 | $this->setExpectedException(ZoneSettingFailException::class); 88 | $this->mockClientAPIClient->method('callAPI')->willReturn(array('errors' => array())); 89 | $this->pluginActions->postPageRule(null, null, null); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/overview-welcome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Test/Unit/Backend/ClientActionsTest.php: -------------------------------------------------------------------------------- 1 | mockConfig = $this->getMockBuilder('\CF\Integration\DefaultConfig') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->mockDataStore = $this->getMockBuilder('\CloudFlare\Plugin\Backend\DataStore') 26 | ->disableOriginalConstructor() 27 | ->getMock(); 28 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 29 | ->disableOriginalConstructor() 30 | ->getMock(); 31 | $this->mockMagentoAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoAPI') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockClientAPIClient = $this->getMockBuilder('\CF\API\Client') 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockRequest = $this->getMockBuilder('\CF\API\Request') 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | $this->mockHttpClient = $this->getMockBuilder('\CF\API\HttpClientInterface') 41 | ->disableOriginalConstructor() 42 | ->getMock(); 43 | 44 | $this->mockIntegrationContext = new MagentoIntegration( 45 | $this->mockConfig, 46 | $this->mockMagentoAPI, 47 | $this->mockDataStore, 48 | $this->mockLogger, 49 | $this->mockHttpClient 50 | ); 51 | $this->clientActions = new ClientActions( 52 | $this->mockIntegrationContext, 53 | $this->mockClientAPIClient, 54 | $this->mockRequest 55 | ); 56 | } 57 | 58 | public function testGetZonesReturnMagentoZoneReturnsZoneIfItExists() 59 | { 60 | $domain = 'domain.com'; 61 | $this->mockMagentoAPI->method('getMagentoDomainName')->willReturn($domain); 62 | $testResult = array('name' => $domain); 63 | $this->mockClientAPIClient->method('callAPI')->willReturn( 64 | array( 65 | "success" => true, 66 | "result" => array($testResult) 67 | ) 68 | ); 69 | $this->mockClientAPIClient->method('responseOk')->willReturn(true); 70 | 71 | $response = $this->clientActions->getZonesReturnMagentoZone(); 72 | 73 | $this->assertEquals($testResult, $response["result"][0]); 74 | $this->assertEquals(1, count($response["result"])); 75 | } 76 | 77 | public function testGetZonesReturnMagentoZoneReturnsInactiveZoneIfItDoesntExists() 78 | { 79 | $this->mockClientAPIClient->method('callAPI')->willReturn( 80 | array( 81 | "success" => true, 82 | "result" => array() 83 | ) 84 | ); 85 | $this->mockClientAPIClient->method('responseOk')->willReturn(true); 86 | 87 | $response = $this->clientActions->getZonesReturnMagentoZone(); 88 | 89 | $this->assertEquals("inactive", $response["result"][0]["status"]); 90 | } 91 | 92 | public function testGetZoneReturnsMagentoZoneReturnsCorrectDomainForListsWithSimilarDomains() 93 | { 94 | $expectedDomain = 'domaindomain.com'; 95 | $domain = array('name' => $expectedDomain); 96 | $domain2 = array('name' => 'domain.com'); 97 | 98 | $this->mockMagentoAPI->method('getMagentoDomainName')->willReturn($expectedDomain); 99 | //order is important, less specific match needs to come before the most specific match 100 | $this->mockClientAPIClient->method('callAPI')->willReturn( 101 | array( 102 | "success" => true, 103 | "result" => array($domain2, $domain) 104 | ) 105 | ); 106 | $this->mockClientAPIClient->method('responseOk')->willReturn(true); 107 | 108 | $response = $this->clientActions->getZonesReturnMagentoZone(); 109 | $this->assertEquals($expectedDomain, $response['result'][0]['name']); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Backend/CacheTags.php: -------------------------------------------------------------------------------- 1 | clientAPI = $clientAPI; 29 | $this->dataStore = $dataStore; 30 | $this->logger = $logger; 31 | } 32 | 33 | /** 34 | * Sets the X-Cache-Tags header(s) on the response 35 | * 36 | * @param $response 37 | * @param $tags 38 | * @return $response 39 | */ 40 | public function setCloudFlareCacheTagsResponseHeader($response, $tags) 41 | { 42 | //hash cache tags to fit more in each header 43 | $tags = $this->hashCacheTags($tags); 44 | $cacheTagHeaderList = $this->get255ByteCacheTagHeaderStringValues($tags); 45 | 46 | /* 47 | * CloudFlare Cache Tags allow for multiple X-Cache-Tags headers but Magento2's 48 | * $response->setHeader() doesn't allow for multiple http headers with the same name 49 | * so we only set the first one in the list 50 | */ 51 | if (count($cacheTagHeaderList) > 0) { 52 | $response->setHeader(self::CLOUDFLARE_CACHE_TAG_HEADER, $cacheTagHeaderList[0]); 53 | $this->logger->debug("CloudFlare header '". self::CLOUDFLARE_CACHE_TAG_HEADER . "' set with value '". $cacheTagHeaderList[0] ."'"); 54 | 55 | if (count($cacheTagHeaderList) > 1) { 56 | $this->logger->debug("Some CloudFlare cache tags were not set because the total length of the list exceeded 255 bytes."); 57 | } 58 | } 59 | 60 | return $response; 61 | } 62 | 63 | /** 64 | * Generate list of X-Cache-Tag header values which are less than 255 bytes each 65 | * 66 | * @param $cacheTagList 67 | * @return array 68 | */ 69 | public function get255ByteCacheTagHeaderStringValues($cacheTagList) 70 | { 71 | $cacheTagHeaderList = array(); 72 | $cacheTagHeader = ""; 73 | 74 | foreach ($cacheTagList as $cacheTag) { 75 | $cacheTagHeaderEncoding = mb_detect_encoding($cacheTagHeader); 76 | $cacheTagEncoding = mb_detect_encoding($cacheTag); 77 | 78 | //Is this cache tag larger than 255 bytes? 79 | if (mb_strlen($cacheTag, $cacheTagEncoding) >= 255) { 80 | array_push($cacheTagHeaderList, mb_strcut($cacheTag, 1, 255)); 81 | } //Would appending the current cache tag to the cache tag header put it over the 255 byte limit? 82 | elseif ((mb_strlen($cacheTagHeader, $cacheTagHeaderEncoding) + mb_strlen(",".$cacheTag, $cacheTagEncoding)) > 255) { 83 | //Start new header 84 | array_push($cacheTagHeaderList, $cacheTagHeader); 85 | $cacheTagHeader = $cacheTag; 86 | } else { 87 | //Append cache tag to cache tag header 88 | if ($cacheTagHeader !== "") { //avoid creating headers that start with a comma. 89 | $cacheTagHeader = $cacheTagHeader . ","; 90 | } 91 | $cacheTagHeader = $cacheTagHeader . $cacheTag; 92 | } 93 | } 94 | 95 | array_push($cacheTagHeaderList, $cacheTagHeader); 96 | 97 | return $cacheTagHeaderList; 98 | } 99 | 100 | /** 101 | * Purge cache by tag 102 | * 103 | * @param $tags 104 | */ 105 | public function purgeCacheTags(array $tags) 106 | { 107 | if (!empty($tags) && $this->dataStore->get(\CF\API\Plugin::SETTING_PLUGIN_SPECIFIC_CACHE_TAG)) { 108 | $tags = $this->hashCacheTags($tags); 109 | $this->clientAPI->zonePurgeCacheByTags($tags); 110 | } 111 | } 112 | 113 | /** 114 | * Purge entire cache 115 | * 116 | * @return mixed 117 | */ 118 | public function purgeCache() 119 | { 120 | return $this->clientAPI->zonePurgeCache(); 121 | } 122 | 123 | /** 124 | * Convert cache tags to 3 character hashes so we can fit more in each header 125 | * 126 | * @param array $tags 127 | * @return array 128 | */ 129 | public function hashCacheTags(array $tags) 130 | { 131 | $hashedCacheTags = array(); 132 | foreach ($tags as $tag) { 133 | array_push($hashedCacheTags, substr(hash('sha256', $tag), 0, 3)); 134 | } 135 | 136 | return $hashedCacheTags; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Adminhtml/Plugin/ProxyTest.php: -------------------------------------------------------------------------------- 1 | mockContext = $this->getMockBuilder('Magento\Backend\App\Action\Context') 22 | ->disableOriginalConstructor() 23 | ->getMock(); 24 | $this->mockDataStore = $this->getMockBuilder('\CloudFlare\Plugin\Backend\DataStore') 25 | ->disableOriginalConstructor() 26 | ->getMock(); 27 | $this->mockHttpClient = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoHttpClient') 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | $this->mockIntegrationContext = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoIntegration') 31 | ->disableOriginalConstructor() 32 | ->getMock(); 33 | $this->mockIntegrationContext->method('getHttpClient')->willReturn($this->mockHttpClient); 34 | $this->mockResultJsonFactory = $this->getMockBuilder('\Magento\Framework\Controller\Result\JsonFactory') 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface') 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | $this->mockMagentoAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\MagentoAPI') 41 | ->disableOriginalConstructor() 42 | ->getMock(); 43 | $this->mockRequestRouter = $this->getMockBuilder('\CF\Router\RequestRouter') 44 | ->disableOriginalConstructor() 45 | ->getMock(); 46 | $this->mockClientAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\ClientAPI') 47 | ->disableOriginalConstructor() 48 | ->getMock(); 49 | $this->mockPluginAPI = $this->getMockBuilder('\CF\API\Plugin') 50 | ->disableOriginalConstructor() 51 | ->getMock(); 52 | 53 | $this->proxy = new Proxy( 54 | $this->mockContext, 55 | $this->mockDataStore, 56 | $this->mockIntegrationContext, 57 | $this->mockResultJsonFactory, 58 | $this->mockLogger, 59 | $this->mockMagentoAPI 60 | ); 61 | 62 | $this->proxy->setRequestRouter($this->mockRequestRouter); 63 | $this->proxy->setClientAPI($this->mockClientAPI); 64 | $this->proxy->setPluginAPI($this->mockPluginAPI); 65 | } 66 | 67 | public function testProcessUrlKeysCallsSetJsonFormTokenOnMagentoRequest() 68 | { 69 | $mockAuth = $this->getMockBuilder('\Magento\Backend\Model\Auth') 70 | ->disableOriginalConstructor() 71 | ->getMock(); 72 | $mockAuth->method('isLoggedIn')->willReturn(false); 73 | $this->mockContext->method('getAuth')->willReturn($mockAuth); 74 | 75 | $mockProxy = $this->getMock( 76 | 'CloudFlare\Plugin\Controller\Adminhtml\Plugin\Proxy', 77 | array('setJsonFormTokenOnMagentoRequest', 'getJSONBody'), 78 | array( 79 | $this->mockContext, 80 | $this->mockDataStore, 81 | $this->mockIntegrationContext, 82 | $this->mockResultJsonFactory, 83 | $this->mockLogger, 84 | $this->mockMagentoAPI, 85 | $this->mockRequestRouter, 86 | $this->mockClientAPI, 87 | $this->mockPluginAPI) 88 | ); 89 | $mockProxy->method('getJSONBody')->willReturn(array(Proxy::FORM_KEY => Proxy::FORM_KEY)); 90 | 91 | $mockProxy->expects($this->once()) 92 | ->method('setJsonFormTokenOnMagentoRequest'); 93 | 94 | $mockProxy->_processUrlKeys(); 95 | } 96 | 97 | public function testSetJsonFormTokenOnMagentoRequestSetsTokenCorrectly() 98 | { 99 | $token = "token"; 100 | 101 | $mockCookieInterface = $this->getMockBuilder('\Magento\Framework\Stdlib\Cookie\CookieReaderInterface') 102 | ->disableOriginalConstructor() 103 | ->getMock(); 104 | 105 | $mockStringUtils = $this->getMockBuilder('\Magento\Framework\Stdlib\StringUtils') 106 | ->disableOriginalConstructor() 107 | ->getMock(); 108 | 109 | $mockRequest = new \Magento\Framework\HTTP\PhpEnvironment\Request($mockCookieInterface, $mockStringUtils, null); 110 | 111 | $this->proxy->setJsonFormTokenOnMagentoRequest($token, $mockRequest); 112 | 113 | 114 | $this->assertEquals($token, $mockRequest->getParam(Proxy::FORM_KEY)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Test/Unit/Backend/CacheTagsTest.php: -------------------------------------------------------------------------------- 1 | mockClientAPI = $this->getMockBuilder('\CloudFlare\Plugin\Backend\ClientAPI') 17 | ->disableOriginalConstructor() 18 | ->getMock(); 19 | $this->mockDataStore = $this->getMockBuilder('\CloudFlare\Plugin\Backend\DataStore') 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | $this->cacheTags = new CacheTags($this->mockClientAPI, $this->mockDataStore, $this->mockLogger); 26 | } 27 | 28 | public function testSetCloudFlareCacheTagsResponseHeaderSetsHeader() 29 | { 30 | $mockResponse = $this->getMockBuilder('\Magento\Framework\App\Response\Http') 31 | ->disableOriginalConstructor() 32 | ->getMock(); 33 | $mockResponse->expects($this->once())->method('setHeader'); 34 | $tags = array("tags"); 35 | $this->cacheTags->setCloudFlareCacheTagsResponseHeader($mockResponse, $tags); 36 | } 37 | 38 | public function testGet255ByteCacheTagHeaderStringValuesReturnsArraySizeOneForInputLessThan255Bytes() 39 | { 40 | $tag = $this->generateByteString(1); 41 | $tags = array($tag); 42 | $response = $this->cacheTags->get255ByteCacheTagHeaderStringValues($tags); 43 | $this->assertEquals(1, count($response)); 44 | $this->assertEquals($tag, $response[0]); 45 | } 46 | 47 | public function testGet255ByteCacheTagHeaderStringValuesConcatsMultipleTags() 48 | { 49 | $tag1 = $this->generateByteString(1); 50 | $tag2 = $this->generateByteString(2); 51 | $tags = array($tag1, $tag2); 52 | $response = $this->cacheTags->get255ByteCacheTagHeaderStringValues($tags); 53 | $this->assertEquals($tag1.",".$tag2, $response[0]); 54 | } 55 | 56 | public function testGet255ByteCacheTagHeaderStringValuesTrimsLargeTags() 57 | { 58 | $tag256 = $this->generateByteString(256); 59 | $this->assertEquals(256, mb_strlen($tag256, mb_detect_encoding($tag256))); 60 | 61 | $response = $this->cacheTags->get255ByteCacheTagHeaderStringValues(array($tag256)); 62 | $this->assertEquals(255, mb_strlen($response[0], mb_detect_encoding($response[0]))); 63 | } 64 | 65 | public function testGet255ByteCacheTagHeaderStringValuesHandlesTagListsLargerThan255Bytes() 66 | { 67 | $tag255 = $this->generateByteString(255); 68 | $tag1 = $this->generateByteString(1); 69 | $tags = array($tag255, $tag1); 70 | $response = $this->cacheTags->get255ByteCacheTagHeaderStringValues($tags); 71 | $this->assertEquals(2, count($response)); 72 | } 73 | 74 | protected function generateByteString($length) 75 | { 76 | $string = ""; 77 | for ($i = 1; $i<=$length; $i++) { 78 | $string = $string."a"; 79 | } 80 | return $string; 81 | } 82 | 83 | public function testPurgeCacheTagsDoesntCallAPIForEmptyArray() 84 | { 85 | $this->mockDataStore->method('get')->willReturn(false); 86 | $tags = array(); 87 | $this->mockClientAPI->expects($this->never())->method('zonePurgeCacheByTags'); 88 | $this->cacheTags->purgeCacheTags($tags); 89 | } 90 | 91 | public function testPurgeCacheTagsDoesntCallAPIIfAutomaticCacheTagDisabled() 92 | { 93 | $this->mockDataStore->method('get')->with(Plugin::SETTING_PLUGIN_SPECIFIC_CACHE_TAG)->willReturn(false); 94 | $tags = array('tagsToPurge'); 95 | $this->mockClientAPI->expects($this->never())->method('zonePurgeCacheByTags'); 96 | $this->cacheTags->purgeCacheTags($tags); 97 | } 98 | 99 | public function testPurgeCacheTagsCallsAPIForNonEmptyArray() 100 | { 101 | $this->mockDataStore->method('get')->with(Plugin::SETTING_PLUGIN_SPECIFIC_CACHE_TAG)->willReturn(true); 102 | $tags = array('tagToPurge'); 103 | $this->mockClientAPI->expects($this->once())->method('zonePurgeCacheByTags'); 104 | $this->cacheTags->purgeCacheTags($tags); 105 | } 106 | 107 | public function testHashCacheTagsHashesWithSha256AndTruncatesToThreeCharacters() 108 | { 109 | $tag = "cacheTag"; 110 | $tags = array($tag); 111 | $expectedHash = substr(hash('sha256', $tag), 0, 3); 112 | $hashedCacheTags = $this->cacheTags->hashCacheTags($tags); 113 | $this->assertEquals($expectedHash, $hashedCacheTags[0]); 114 | } 115 | 116 | public function testPurgeCacheCallsPurgeCacheAPI() 117 | { 118 | $this->mockClientAPI->expects($this->once())->method('zonePurgeCache'); 119 | $this->cacheTags->purgeCache(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Backend/MagentoAPI.php: -------------------------------------------------------------------------------- 1 | configReader = $configReader; 28 | $this->keyValueFactory = $keyValueFactory; 29 | $this->logger = $logger; 30 | $this->storeManager = $storeManager; 31 | 32 | $this->magentoConfig = $this->configReader->load(); 33 | } 34 | 35 | public function getMagentoDomainName() 36 | { 37 | 38 | //getBaseUrl() has format (http | https)://(www)[DOMAIN NAME]/ 39 | //need [DOMAIN NAME] 40 | $domainName = $this->storeManager->getStore()->getBaseUrl(); 41 | $domainName = str_replace("http://", "", $domainName); 42 | $domainName = str_replace("https://", "", $domainName); 43 | $domainName = str_replace("www.", "", $domainName); 44 | $domainName = rtrim($domainName, "/"); 45 | 46 | return $domainName; 47 | } 48 | 49 | public function getMagentoAdminPath() 50 | { 51 | return $this->magentoConfig['backend']['frontName']; 52 | } 53 | 54 | /** 55 | * @param $key 56 | * @return null 57 | */ 58 | public function getValue($key) 59 | { 60 | try { 61 | $keyValueModel = $this->keyValueFactory->create(); 62 | $keyValueModel->load($key, InstallSchema::CLOUDFLARE_DATA_TABLE_KEY_COLUMN); 63 | $result = $keyValueModel->getData(); 64 | 65 | if (array_key_exists(InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, $result)) { 66 | return $result[InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN]; 67 | } 68 | } catch (\Zend_Db_Statement_Exception $e) { 69 | $this->logger->error($e->getMessage() . $e->getTraceAsString()); 70 | } 71 | 72 | return null; 73 | } 74 | 75 | /** 76 | * @param $key 77 | * @param $value 78 | * @return bool 79 | */ 80 | public function setValue($key, $value) 81 | { 82 | try { 83 | $keyValueModel = $this->keyValueFactory->create(); 84 | $keyValueModel->load($key, InstallSchema::CLOUDFLARE_DATA_TABLE_KEY_COLUMN); 85 | if (empty($keyValueModel->getData())) { 86 | //key doesn't exist yet, create new 87 | $keyValueModel = $this->keyValueFactory->create(); 88 | $keyValueModel->setData(InstallSchema::CLOUDFLARE_DATA_TABLE_KEY_COLUMN, $key); 89 | $keyValueModel->setData(InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, $value); 90 | $keyValueModel->save(); 91 | } else { 92 | //update existing key 93 | $keyValueModel->setData(InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, $value); 94 | $keyValueModel->save(); 95 | } 96 | return true; 97 | } catch (\Zend_Db_Statement_Exception $e) { 98 | $this->logger->error($e->getMessage() . $e->getTraceAsString()); 99 | } 100 | return false; 101 | } 102 | 103 | /** 104 | * @param $domainName 105 | * @return mixed 106 | */ 107 | public function getDNSRecords($domainName) 108 | { 109 | return null; 110 | } 111 | 112 | /** 113 | * @param $domainName 114 | * @param DNSRecord $DNSRecord 115 | * @return mixed 116 | */ 117 | public function addDNSRecord($domainName, DNSRecord $DNSRecord) 118 | { 119 | return null; 120 | } 121 | 122 | /** 123 | * @param $domain_name 124 | * @param DNSRecord $DNSRecord 125 | * @return mixed 126 | */ 127 | public function editDNSRecord($domain_name, DNSRecord $DNSRecord) 128 | { 129 | return null; 130 | } 131 | 132 | /** 133 | * @param $domain_name 134 | * @param DNSRecord $DNSRecord 135 | * @return mixed 136 | */ 137 | public function removeDNSRecord($domain_name, DNSRecord $DNSRecord) 138 | { 139 | return null; 140 | } 141 | 142 | /** 143 | * @return mixed 144 | */ 145 | public function getHostAPIKey() 146 | { 147 | return null; 148 | } 149 | 150 | /** 151 | * @param null $userId 152 | * @return mixed 153 | */ 154 | public function getDomainList($userId = null) 155 | { 156 | return null; 157 | } 158 | 159 | /** 160 | * @return mixed 161 | */ 162 | public function getUserId() 163 | { 164 | return null; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Plugin/Proxy.php: -------------------------------------------------------------------------------- 1 | dataStore = $dataStore; 54 | $this->integrationContext = $integrationContext; 55 | $this->logger = $logger; 56 | $this->magentoAPI = $magentoAPI; 57 | $this->resultJsonFactory = $resultJsonFactory; 58 | 59 | //PI-1074 - new is bad but a necessary work around here. 60 | $this->clientAPI = new ClientAPI($this->integrationContext); 61 | $this->pluginAPI = new Plugin($this->integrationContext); 62 | 63 | $this->requestRouter = new RequestRouter($this->integrationContext); 64 | $this->requestRouter->addRouter($this->clientAPI, ClientRoutes::$routes); 65 | $this->requestRouter->addRouter($this->pluginAPI, PluginRoutes::getRoutes(\CF\API\PluginRoutes::$routes)); 66 | 67 | // php://input can only be read once 68 | $decodedJson = json_decode(file_get_contents('php://input'), true); 69 | if (json_last_error() !== 0) { 70 | $this->logger->error("Error decoding JSON: ". json_last_error_msg()); 71 | } 72 | $this->jsonBody = $decodedJson; 73 | 74 | parent::__construct($context); 75 | } 76 | 77 | /** 78 | * @return \Magento\Framework\Controller\Result\Json 79 | */ 80 | public function execute() 81 | { 82 | $result = $this->resultJsonFactory->create(); 83 | 84 | $magentoRequest = $this->getRequest(); 85 | $method = $magentoRequest->getMethod(); 86 | $parameters = $magentoRequest->getParams(); 87 | $body = $this->getJsonBody(); 88 | $path = (strtoupper($method === "GET") ? $parameters['proxyURL'] : $body['proxyURL']); 89 | 90 | $request = new Request($method, $path, $parameters, $body); 91 | 92 | $response = $this->requestRouter->route($request); 93 | 94 | return $result->setData($response); 95 | } 96 | 97 | public function getJsonBody() 98 | { 99 | return $this->jsonBody; 100 | } 101 | 102 | /** 103 | * @param $jsonBody 104 | */ 105 | public function setJsonBody($jsonBody) 106 | { 107 | $this->jsonBody = $jsonBody; 108 | } 109 | 110 | /* 111 | * Magento CSRF validation can't find the CSRF Token "form_key" if its in the JSON 112 | * so we copy it from the JSON body to the Magento request parameters. 113 | */ 114 | public function _processUrlKeys() 115 | { 116 | $requestJsonBody = $this->getJsonBody(); 117 | if ($requestJsonBody !== null && array_key_exists(self::FORM_KEY, $requestJsonBody)) { 118 | $this->setJsonFormTokenOnMagentoRequest($requestJsonBody[self::FORM_KEY], $this->getRequest()); 119 | } 120 | return parent::_processUrlKeys(); 121 | } 122 | 123 | /** 124 | * @param $token "form_key" 125 | * @param $request 126 | */ 127 | public function setJsonFormTokenOnMagentoRequest($token, $request) 128 | { 129 | $parameters = $request->getParams(); 130 | $parameters[self::FORM_KEY] = $token; 131 | $request->setParams($parameters); 132 | return $request; 133 | } 134 | 135 | /** 136 | * @param RequestRouter $requestRouter 137 | */ 138 | public function setRequestRouter(RequestRouter $requestRouter) 139 | { 140 | $this->requestRouter = $requestRouter; 141 | } 142 | 143 | /** 144 | * @param ClientAPI $clientAPI 145 | */ 146 | public function setClientAPI(ClientAPI $clientAPI) 147 | { 148 | $this->clientAPI = $clientAPI; 149 | } 150 | 151 | /** 152 | * @param Plugin $pluginAPI 153 | */ 154 | public function setPluginAPI(Plugin $pluginAPI) 155 | { 156 | $this->pluginAPI = $pluginAPI; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Test/Unit/Backend/MagentoAPITest.php: -------------------------------------------------------------------------------- 1 | mockConfigReader = $this->getMockBuilder('\Magento\Framework\App\DeploymentConfig\Reader') 21 | ->disableOriginalConstructor() 22 | ->getMock(); 23 | $this->mockKeyValueFactory = $this->getMockBuilder('\CloudFlare\Plugin\Model\KeyValueFactory') 24 | ->disableOriginalConstructor() 25 | ->setMethods(['create']) 26 | ->getMock(); 27 | $this->mockKeyValueModel = $this->getMockBuilder('\CloudFlare\Plugin\Model\KeyValue') 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | $this->mockKeyValueFactory->method('create')->willReturn($this->mockKeyValueModel); 31 | $this->mockLogger = $this->getMockBuilder('\Psr\Log\LoggerInterface') 32 | ->disableOriginalConstructor() 33 | ->getMock(); 34 | $this->mockStoreManager = $this->getMockBuilder('\Magento\Store\Model\StoreManagerInterface') 35 | ->disableOriginalConstructor() 36 | ->getMock(); 37 | $this->mockStore = $this->getMockBuilder('\Magento\Store\Model\Data\StoreConfig') 38 | ->disableOriginalConstructor() 39 | ->getMock(); 40 | $this->mockStoreManager->method('getStore')->willReturn($this->mockStore); 41 | $this->magentoAPI = new MagentoAPI( 42 | $this->mockConfigReader, 43 | $this->mockKeyValueFactory, 44 | $this->mockStoreManager, 45 | $this->mockLogger 46 | ); 47 | } 48 | 49 | public function testGetMagentoDomainNameRemovesHttpHttpsSlashFromBaseUrl() 50 | { 51 | $this->mockStore->method('getBaseUrl')->willReturn("http://www.site.com/"); 52 | $fqdn = $this->magentoAPI->getMagentoDomainName(); 53 | $this->assertEquals("site.com", $fqdn); 54 | } 55 | 56 | public function testGetMagentoAdminPathReturnsConfigPath() 57 | { 58 | $configPath = "configPath"; 59 | $this->mockConfigReader->method('load')->willReturn( 60 | array( 61 | 'backend' => array('frontName' => $configPath) 62 | ) 63 | ); 64 | $this->magentoAPI = new MagentoAPI( 65 | $this->mockConfigReader, 66 | $this->mockKeyValueFactory, 67 | $this->mockStoreManager, 68 | $this->mockLogger 69 | ); 70 | 71 | $this->assertEquals($configPath, $this->magentoAPI->getMagentoAdminPath()); 72 | } 73 | 74 | public function testGetValueReturnsNullForBadKey() 75 | { 76 | $key = "key"; 77 | 78 | $this->mockKeyValueModel->method('load') 79 | ->with($key, "key") 80 | ->willReturn(true); 81 | $this->mockKeyValueModel->method('getData')->willReturn(array()); 82 | 83 | $result = $this->magentoAPI->getValue($key); 84 | $this->assertNull($result); 85 | } 86 | 87 | public function testGetValueReturnsCorrectValue() 88 | { 89 | $key = "key"; 90 | $value = "value"; 91 | 92 | $this->mockKeyValueModel->method('load') 93 | ->with($key, InstallSchema::CLOUDFLARE_DATA_TABLE_KEY_COLUMN) 94 | ->willReturn(true); 95 | $this->mockKeyValueModel->method('getData')->willReturn(array( 96 | 'id' => 'id', 97 | 'key' => $key, 98 | 'value' => $value 99 | )); 100 | 101 | $result = $this->magentoAPI->getValue($key); 102 | $this->assertEquals($value, $result); 103 | } 104 | 105 | public function testSetValueCreatesNewKeyIfItDoesntExist() 106 | { 107 | $key = "key"; 108 | $value = "value"; 109 | 110 | $this->mockKeyValueModel->method('getData')->willReturn(array()); 111 | 112 | $this->mockKeyValueModel->expects($this->at(1)) 113 | ->method('create'); 114 | $this->mockKeyValueModel->expects($this->at(2)) 115 | ->method('setData') 116 | ->with(InstallSchema::CLOUDFLARE_DATA_TABLE_KEY_COLUMN, $key); 117 | $this->mockKeyValueModel->expects($this->at(3)) 118 | ->method('setData') 119 | ->with(InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, $value); 120 | $this->mockKeyValueModel->expects($this->once())->method('save'); 121 | 122 | $this->magentoAPI->setValue($key, $value); 123 | } 124 | 125 | public function testSetValueUpdatesExistingKey() 126 | { 127 | $id = "1"; 128 | $key = "key"; 129 | $value = "value"; 130 | 131 | $this->mockKeyValueModel->method('getData')->willReturn(array( 132 | $id => $id, 133 | $key => $key, 134 | $value => $value 135 | )); 136 | 137 | $this->mockKeyValueModel->expects($this->at(1)) 138 | ->method('create'); 139 | $this->mockKeyValueModel->expects($this->once()) 140 | ->method('setData') 141 | ->with(InstallSchema::CLOUDFLARE_DATA_TABLE_VALUE_COLUMN, $value); 142 | $this->mockKeyValueModel->expects($this->once())->method('save'); 143 | 144 | $this->magentoAPI->setValue($key, $value); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/fontawesome-cloudflare.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Backend/PluginActions.php: -------------------------------------------------------------------------------- 1 | clientAPI = new clientAPI($defaultIntegration); 30 | } 31 | 32 | /* 33 | * PATCH /plugin/:id/settings/default_settings 34 | */ 35 | public function applyDefaultSettings() 36 | { 37 | $pathArray = explode('/', $this->request->getUrl()); 38 | $zoneId = $pathArray[1]; 39 | 40 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/security_level', array(), array('value' => 'medium')); 41 | 42 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/cache_level', array(), array('value' => 'basic')); 43 | 44 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/minify', array(), array('value' => array('css' => 'on', 'html' => 'off', 'js' => 'off'))); 45 | 46 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/browser_cache_ttl', array(), array('value' => 14400)); //4 hours 47 | 48 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/always_online', array(), array('value' => 'on')); 49 | 50 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/development_mode', array(), array('value' => 'off')); 51 | 52 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/ipv6', array(), array('value' => 'off')); 53 | 54 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/websockets', array(), array('value' => 'on')); 55 | 56 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/ip_geolocation', array(), array('value' => 'on')); 57 | 58 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/email_obfuscation', array(), array('value' => 'on')); 59 | 60 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/server_side_exclude', array(), array('value' => 'on')); 61 | 62 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/hotlink_protection', array(), array('value' => 'off')); 63 | 64 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/polish', array(), array('value' => 'off')); 65 | 66 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/mirage', array(), array('value' => 'off')); 67 | 68 | $this->patchZoneSetting('zones/'. $zoneId .'/settings/rocket_loader', array(), array('value' => 'off')); 69 | 70 | $adminUrlPattern = $this->integrationAPI->getMagentoDomainName() . '/' . $this->integrationAPI->getMagentoAdminPath() . "*"; 71 | $checkoutUrlPattern = $this->integrationAPI->getMagentoDomainName() . '/checkout*'; 72 | $setupUrlPattern = $this->integrationAPI->getMagentoDomainName() . '/setup*'; 73 | 74 | $this->postPageRule($zoneId, $this->createPageRuleDisablePerformanceCacheBypassJsonBody($adminUrlPattern)); 75 | $this->postPageRule($zoneId, $this->createPageRuleDisablePerformanceCacheBypassJsonBody($checkoutUrlPattern)); 76 | $this->postPageRule($zoneId, $this->createPageRuleDisablePerformanceCacheBypassJsonBody($setupUrlPattern)); 77 | } 78 | 79 | /** 80 | * @param $url 81 | * @param $parameters 82 | * @param $body 83 | * @return bool 84 | * @throws ZoneSettingFailException 85 | */ 86 | public function patchZoneSetting($url, $parameters, $body) 87 | { 88 | $response = $this->clientAPI->callAPI(new \CF\API\Request('PATCH', $url, $parameters, $body)); 89 | 90 | if (!$this->clientAPI->responseOk($response)) { 91 | foreach ($response['errors'] as $error) { 92 | if (in_array($error['message'], self::$upgradePlanErrors)) { 93 | //error is related to upgrading the plan. 94 | return true; 95 | } 96 | } 97 | throw new ZoneSettingFailException(); 98 | } 99 | } 100 | 101 | /** 102 | * @param $zoneId 103 | * @param $body 104 | * @throws ZoneSettingFailException 105 | */ 106 | public function postPageRule($zoneId, $body) 107 | { 108 | $response = $this->clientAPI->callAPI(new \CF\API\Request('POST', 'zones/'. $zoneId .'/pagerules', array(), $body)); 109 | if (!$this->clientAPI->responseOk($response)) { 110 | throw new ZoneSettingFailException(); 111 | } 112 | } 113 | 114 | /** 115 | * @param $urlPattern 116 | * @return array 117 | */ 118 | public function createPageRuleDisablePerformanceCacheBypassJsonBody($urlPattern) 119 | { 120 | return array( 121 | 'targets' => array( 122 | array( 123 | 'target' => 'url', 124 | 'constraint' => array( 125 | 'operator' => 'matches', 126 | 'value' => $urlPattern 127 | ) 128 | ) 129 | ), 130 | 'actions' => array( 131 | array( 132 | 'id' => 'disable_performance' 133 | ), 134 | array( 135 | 'id' => 'cache_level', 136 | 'value' => 'bypass' 137 | ) 138 | ), 139 | 'status' => 'active' 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/logo.svg: -------------------------------------------------------------------------------- 1 | sept_logo2 -------------------------------------------------------------------------------- /view/adminhtml/web/assets/logo-symbol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/overview-welcome-yjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Artwork 5 | Created with Sketch. 6 | 7 | 8 | 9 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/yjs-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/adminhtml/web/assets/logo-reverse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /view/adminhtml/web/stylesheets/hacks.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Currently we're using two style sheets while WWW restyles from Backbone > React. 3 | * Eventually all the styles will live in components.css but until that day we'll hack 4 | * everything here. 5 | */ 6 | 7 | 8 | /* Fix black fill in Analytics */ 9 | .c3-chart { 10 | fill: transparent; 11 | } 12 | 13 | /* center cf-component-toggle */ 14 | .cf-toggle { 15 | margin: 0 auto; 16 | } 17 | 18 | .cf-icon--loading::before { 19 | width: 16px; 20 | } 21 | 22 | .cf-toggle::before, .cf-toggle::after { 23 | font-size: 10px; 24 | } 25 | 26 | #cf-login-page { 27 | max-width: 400px; 28 | margin: 0 auto; 29 | } 30 | #cf-login-page form { 31 | margin-bottom: 20px; 32 | } 33 | 34 | #cf-login-page h3 { 35 | text-align: center; 36 | } 37 | 38 | #cf-login-page .cf-form__fieldset_content { 39 | background-color: #FFF; 40 | border-color: #FFF; 41 | } 42 | 43 | #cf-login-page legend { 44 | display: none; 45 | } 46 | 47 | #cf-login-page .cf-layout__row { 48 | margin-top:15px; 49 | } 50 | 51 | #cf-login-page .cf-layout__column { 52 | text-align: center; 53 | } 54 | 55 | /* Fix z-index issue with Magento Nav */ 56 | .cloudflare-partners .header { 57 | z-index: 1; 58 | } 59 | 60 | /* cf-component-select */ 61 | /** 62 | * React Select 63 | * ============ 64 | * Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/ 65 | * https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs 66 | * MIT License: https://github.com/keystonejs/react-select 67 | */ 68 | .Select { 69 | position: relative; 70 | } 71 | .Select, 72 | .Select div, 73 | .Select input, 74 | .Select span { 75 | -webkit-box-sizing: border-box; 76 | -moz-box-sizing: border-box; 77 | box-sizing: border-box; 78 | } 79 | .Select.is-disabled > .Select-control { 80 | background-color: #f9f9f9; 81 | } 82 | .Select.is-disabled > .Select-control:hover { 83 | box-shadow: none; 84 | } 85 | .Select.is-disabled .Select-arrow-zone { 86 | cursor: default; 87 | pointer-events: none; 88 | } 89 | .Select-control { 90 | background-color: #fff; 91 | border-color: #d9d9d9 #ccc #b3b3b3; 92 | border-radius: 4px; 93 | border: 1px solid #ccc; 94 | color: #333; 95 | cursor: default; 96 | display: table; 97 | height: 36px; 98 | outline: none; 99 | overflow: hidden; 100 | position: relative; 101 | width: 100%; 102 | } 103 | .Select-control:hover { 104 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); 105 | } 106 | .is-searchable.is-open > .Select-control { 107 | cursor: text; 108 | } 109 | .is-open > .Select-control { 110 | border-bottom-right-radius: 0; 111 | border-bottom-left-radius: 0; 112 | background: #fff; 113 | border-color: #b3b3b3 #ccc #d9d9d9; 114 | } 115 | .is-open > .Select-control > .Select-arrow { 116 | border-color: transparent transparent #999; 117 | border-width: 0 5px 5px; 118 | } 119 | .is-searchable.is-focused:not(.is-open) > .Select-control { 120 | cursor: text; 121 | } 122 | .is-focused:not(.is-open) > .Select-control { 123 | border-color: #007eff; 124 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 3px rgba(0, 126, 255, 0.1); 125 | } 126 | .Select-placeholder, 127 | :not(.Select--multi) > .Select-control .Select-value { 128 | bottom: 0; 129 | color: #000; 130 | left: 0; 131 | line-height: 34px; 132 | padding-left: 10px; 133 | padding-right: 10px; 134 | position: absolute; 135 | right: 0; 136 | top: 0; 137 | max-width: 100%; 138 | overflow: hidden; 139 | text-overflow: ellipsis; 140 | white-space: nowrap; 141 | } 142 | .has-value:not(.Select--multi) > .Select-control > .Select-value .Select-value-label, 143 | .has-value.is-pseudo-focused:not(.Select--multi) > .Select-control > .Select-value .Select-value-label { 144 | color: #333; 145 | } 146 | .has-value:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label, 147 | .has-value.is-pseudo-focused:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label { 148 | cursor: pointer; 149 | text-decoration: none; 150 | } 151 | .has-value:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label:hover, 152 | .has-value.is-pseudo-focused:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label:hover, 153 | .has-value:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label:focus, 154 | .has-value.is-pseudo-focused:not(.Select--multi) > .Select-control > .Select-value a.Select-value-label:focus { 155 | color: #007eff; 156 | outline: none; 157 | text-decoration: underline; 158 | } 159 | .Select-input { 160 | height: 34px; 161 | padding-left: 10px; 162 | padding-right: 10px; 163 | vertical-align: middle; 164 | } 165 | .Select-input > input { 166 | background: none transparent; 167 | border: 0 none; 168 | box-shadow: none; 169 | cursor: default; 170 | display: inline-block; 171 | font-family: inherit; 172 | font-size: inherit; 173 | height: 34px; 174 | margin: 0; 175 | outline: none; 176 | padding: 0; 177 | -webkit-appearance: none; 178 | } 179 | .is-focused .Select-input > input { 180 | cursor: text; 181 | } 182 | .has-value.is-pseudo-focused .Select-input { 183 | opacity: 0; 184 | } 185 | .Select-control:not(.is-searchable) > .Select-input { 186 | outline: none; 187 | } 188 | .Select-loading-zone { 189 | cursor: pointer; 190 | display: table-cell; 191 | position: relative; 192 | text-align: center; 193 | vertical-align: middle; 194 | width: 16px; 195 | } 196 | .Select-loading { 197 | -webkit-animation: Select-animation-spin 400ms infinite linear; 198 | -o-animation: Select-animation-spin 400ms infinite linear; 199 | animation: Select-animation-spin 400ms infinite linear; 200 | width: 16px; 201 | height: 16px; 202 | box-sizing: border-box; 203 | border-radius: 50%; 204 | border: 2px solid #ccc; 205 | border-right-color: #333; 206 | display: inline-block; 207 | position: relative; 208 | vertical-align: middle; 209 | } 210 | .Select-clear-zone { 211 | -webkit-animation: Select-animation-fadeIn 200ms; 212 | -o-animation: Select-animation-fadeIn 200ms; 213 | animation: Select-animation-fadeIn 200ms; 214 | color: #999; 215 | cursor: pointer; 216 | display: table-cell; 217 | position: relative; 218 | text-align: center; 219 | vertical-align: middle; 220 | width: 17px; 221 | } 222 | .Select-clear-zone:hover { 223 | color: #D0021B; 224 | } 225 | .Select-clear { 226 | display: inline-block; 227 | font-size: 18px; 228 | line-height: 1; 229 | } 230 | .Select--multi .Select-clear-zone { 231 | width: 17px; 232 | } 233 | .Select-arrow-zone { 234 | cursor: pointer; 235 | display: table-cell; 236 | position: relative; 237 | text-align: center; 238 | vertical-align: middle; 239 | width: 25px; 240 | padding-right: 5px; 241 | } 242 | .Select-arrow { 243 | border-color: #999 transparent transparent; 244 | border-style: solid; 245 | border-width: 5px 5px 2.5px; 246 | display: inline-block; 247 | height: 0; 248 | width: 0; 249 | } 250 | .is-open .Select-arrow, 251 | .Select-arrow-zone:hover > .Select-arrow { 252 | border-top-color: #666; 253 | } 254 | @-webkit-keyframes Select-animation-fadeIn { 255 | from { 256 | opacity: 0; 257 | } 258 | to { 259 | opacity: 1; 260 | } 261 | } 262 | @keyframes Select-animation-fadeIn { 263 | from { 264 | opacity: 0; 265 | } 266 | to { 267 | opacity: 1; 268 | } 269 | } 270 | .Select-menu-outer { 271 | border-bottom-right-radius: 4px; 272 | border-bottom-left-radius: 4px; 273 | background-color: #fff; 274 | border: 1px solid #ccc; 275 | border-top-color: #e6e6e6; 276 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); 277 | box-sizing: border-box; 278 | margin-top: -1px; 279 | max-height: 200px; 280 | position: absolute; 281 | top: 100%; 282 | width: 100%; 283 | z-index: 214748364; 284 | -webkit-overflow-scrolling: touch; 285 | } 286 | .Select-menu { 287 | max-height: 198px; 288 | overflow-y: auto; 289 | } 290 | .Select-option { 291 | box-sizing: border-box; 292 | background-color: #fff; 293 | color: #666666; 294 | cursor: pointer; 295 | display: block; 296 | padding: 8px 10px; 297 | } 298 | .Select-option:last-child { 299 | border-bottom-right-radius: 4px; 300 | border-bottom-left-radius: 4px; 301 | } 302 | .Select-option.is-focused { 303 | background-color: rgba(0, 126, 255, 0.08); 304 | color: #333; 305 | } 306 | .Select-option.is-disabled { 307 | color: #cccccc; 308 | cursor: default; 309 | } 310 | .Select-noresults { 311 | box-sizing: border-box; 312 | color: #999999; 313 | cursor: default; 314 | display: block; 315 | padding: 8px 10px; 316 | } 317 | .Select--multi .Select-input { 318 | vertical-align: middle; 319 | margin-left: 10px; 320 | padding: 0; 321 | } 322 | .Select--multi.has-value .Select-input { 323 | margin-left: 5px; 324 | } 325 | .Select--multi .Select-value { 326 | background-color: rgba(0, 126, 255, 0.08); 327 | border-radius: 2px; 328 | border: 1px solid rgba(0, 126, 255, 0.24); 329 | color: #007eff; 330 | display: inline-block; 331 | font-size: 0.9em; 332 | line-height: 1.4; 333 | margin-left: 5px; 334 | margin-top: 5px; 335 | vertical-align: top; 336 | } 337 | .Select--multi .Select-value-icon, 338 | .Select--multi .Select-value-label { 339 | display: inline-block; 340 | vertical-align: middle; 341 | } 342 | .Select--multi .Select-value-label { 343 | border-bottom-right-radius: 2px; 344 | border-top-right-radius: 2px; 345 | cursor: default; 346 | padding: 2px 5px; 347 | } 348 | .Select--multi a.Select-value-label { 349 | color: #007eff; 350 | cursor: pointer; 351 | text-decoration: none; 352 | } 353 | .Select--multi a.Select-value-label:hover { 354 | text-decoration: underline; 355 | } 356 | .Select--multi .Select-value-icon { 357 | cursor: pointer; 358 | border-bottom-left-radius: 2px; 359 | border-top-left-radius: 2px; 360 | border-right: 1px solid rgba(0, 126, 255, 0.24); 361 | padding: 1px 5px 3px; 362 | } 363 | .Select--multi .Select-value-icon:hover, 364 | .Select--multi .Select-value-icon:focus { 365 | background-color: rgba(0, 113, 230, 0.08); 366 | color: #0071e6; 367 | } 368 | .Select--multi .Select-value-icon:active { 369 | background-color: rgba(0, 126, 255, 0.24); 370 | } 371 | .Select--multi.is-disabled .Select-value { 372 | background-color: #fcfcfc; 373 | border: 1px solid #e3e3e3; 374 | color: #333; 375 | } 376 | .Select--multi.is-disabled .Select-value-icon { 377 | cursor: not-allowed; 378 | border-right: 1px solid #e3e3e3; 379 | } 380 | .Select--multi.is-disabled .Select-value-icon:hover, 381 | .Select--multi.is-disabled .Select-value-icon:focus, 382 | .Select--multi.is-disabled .Select-value-icon:active { 383 | background-color: #fcfcfc; 384 | } 385 | @keyframes Select-animation-spin { 386 | to { 387 | transform: rotate(1turn); 388 | } 389 | } 390 | @-webkit-keyframes Select-animation-spin { 391 | to { 392 | -webkit-transform: rotate(1turn); 393 | } 394 | } 395 | 396 | /* end cf-component-select */ 397 | -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/cloudflare-font.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /view/adminhtml/web/lang/en.js: -------------------------------------------------------------------------------- 1 | { 2 | "component.clientLogin.form.email": "Email", 3 | "component.clientLogin.form.apiKeyHelp": "Already have an account? Get your API Key from", 4 | "component.clientLogin.form.apiKey": "API Key", 5 | "component.clientLogin.form.button": "Save API Credentials", 6 | "component.clientLogin.form.signUp": "Sign up", 7 | "component.clientLogin.form.title": "Enter Cloudflare API Credentials", 8 | "component.clientLogin.cloudflare.description": "New to Cloudflare? Sign up at", 9 | "component.customcardcontrol.upgrade": "Upgrade to", 10 | "component.login.form.email": "Email", 11 | "component.login.form.forgotPassword": "Forgot your password?", 12 | "component.login.form.password": "Password", 13 | "component.login.form.button": "Log in", 14 | "component.login.form.signUp": "Sign up", 15 | "component.login.form.title": "Log in to Cloudflare", 16 | "component.login.cloudflare.description": "Cloudflare makes more than 2,000,000 web properties faster and safer. Join today!", 17 | "component.marketingFeature.cdn.title": "CDN", 18 | "component.marketingFeature.cdn.description": "Distribute your content around the world so it’s closer to your visitors (speeding up your site).", 19 | "component.marketingFeature.optimization.title": "Optimization", 20 | "component.marketingFeature.optimization.description": "Web pages with ad servers and third party widgets load snappy on both mobile and computers.", 21 | "component.marketingFeature.security.title": "Security", 22 | "component.marketingFeature.security.description": "Protect your website from online threats with our enterprise-grade Website Application Firewall (WAF).", 23 | "component.marketingFeature.ddos.title": "DDoS Protection", 24 | "component.marketingFeature.ddos.description": "Ensure your website is protected against DDoS attacks using our advanced service.", 25 | "container.activeZoneSelector.activeZone": "Active Zone", 26 | "container.activationCheckCard.button": "Recheck Nameservers", 27 | "container.activationCheckCard.description": "Allow up to 24 hours for this change to be processed. There will be no downtime when you switch your name servers. Traffic will gracefully roll from your old name servers to the new name servers without interruption. Your site will remain available throughout the switch", 28 | "container.activationCheckCard.nameServers": "Please ensure your website is using the nameservers provided:", 29 | "container.activationCheckCard.title": "Activation Check", 30 | "container.activationCheckCard.status": "Status: {status}", 31 | "container.activationCheckCard.success": "Your domain is now queued up to be rescanned. Please check back in a few hours.", 32 | "container.advanceddos.title": "Advance DDoS", 33 | "container.advanceddos.description": "Cloudflare will stand in front of your website regardless of attack size or duration.", 34 | "container.advanceddos.value": "Visit Cloudflare.com", 35 | "container.alwaysOnlineCard.title": "Always Online™", 36 | "container.alwaysOnlineCard.description": "If your server goes down, Cloudflare will serve your website's static pages from our cache.", 37 | "containers.analyticsPage.cached": "Cached", 38 | "containers.analyticsPage.uncached": "Uncached", 39 | "containers.analyticsPage.threats": "Threats", 40 | "containers.analyticsPage.uniques": "Unique Visitors", 41 | "container.analyticsPage.tabs.requests": "Requests", 42 | "container.analyticsPage.tabs.requests.title": "Request Through Cloudflare", 43 | "container.analyticsPage.tabs.requests.total": "Total Requests", 44 | "container.analyticsPage.tabs.requests.cached": "Total Requests", 45 | "container.analyticsPage.tabs.requests.uncached": "Total Requests", 46 | "container.analyticsPage.tabs.bandwidth": "Bandwidth", 47 | "container.analyticsPage.tabs.bandwidth.title": "Bandwidth", 48 | "container.analyticsPage.tabs.bandwidth.total": "Total Bandwidth", 49 | "container.analyticsPage.tabs.bandwidth.cached": "Cached Bandwidth", 50 | "container.analyticsPage.tabs.bandwidth.uncached": "Uncached Bandwidth", 51 | "container.analyticsPage.tabs.uniques": "Unique Visitors", 52 | "container.analyticsPage.tabs.uniques.title": "Unique Visitors", 53 | "container.analyticsPage.tabs.uniques.total": "Total Unique Visitors", 54 | "container.analyticsPage.tabs.uniques.maximum": "Maximum Unique Visitors", 55 | "container.analyticsPage.tabs.uniques.minimum": "Minimum Unique Visitors", 56 | "container.analyticsPage.tabs.threats": "Threats", 57 | "container.analyticsPage.tabs.threats.title": "Threats", 58 | "container.analyticsPage.tabs.threats.total": "Total Threats", 59 | "container.analyticsPage.tabs.threats.country": "Top Country", 60 | "container.analyticsPage.tabs.threats.type": "Top Threat Type", 61 | "container.analyticsPage.title": "Analytics", 62 | "container.analyticCard.duration": "Last Month", 63 | "container.analyticCard.bandwidth.title": "Bandwidth Saved", 64 | "container.analyticCard.bandwidth.datatype": "Bandwidth", 65 | "container.analyticCard.bandwidth.firstText": "saved", 66 | "container.analyticCard.bandwidth.secondText": "total bandwidth", 67 | "container.analyticCard.ssl.title": "Traffic Served Over SSL", 68 | "container.analyticCard.ssl.datatype": "SSL", 69 | "container.analyticCard.ssl.firstText": "SSL secured request", 70 | "container.analyticCard.ssl.secondText": "unsecured requests", 71 | "container.App.version": "Version: {version}", 72 | "container.applydefaultsettingscard.title": "Apply Default Settings", 73 | "container.applydefaultsettingscard.description": "Apply Cloudflare recommended settings to ensure optimal site performance.", 74 | "container.applydefaultsettingscard.button": "Apply", 75 | "container.applydefaultsettingscard.success": "Your default settings have been successfully set.", 76 | "container.appNavigation.home": "Home", 77 | "container.appNavigation.moresettings": "Settings", 78 | "container.appNavigation.domainsOverview": "Domains", 79 | "container.appNavigation.analytics": "Analytics", 80 | "container.appNavigation.performance": "Performance", 81 | "container.appNavigation.security": "Security", 82 | "container.appNavigation.support": "Support", 83 | "container.browserCacheTTLCard.description": "Determine the length of time Cloudflare instructs a visitor's browser to cache files. During this period, the browser loads the files from its local cache, speeding up page loads.", 84 | "container.browserCacheTTLCard.title": "Browser Cache Expiration", 85 | "container.browserIntegrityCheckCard.description": "Evaluate HTTP headers from your visitors browser for threats. If a threat is found a block page will be delivered.", 86 | "container.browserIntegrityCheckCard.title": "Browser Integrity Check", 87 | "container.browserIntegrityCheckCard.twoHours": "2 hours", 88 | "container.browserIntegrityCheckCard.threeHours": "3 hours", 89 | "container.browserIntegrityCheckCard.fourHours": "4 hours", 90 | "container.browserIntegrityCheckCard.fiveHours": "5 hours", 91 | "container.browserIntegrityCheckCard.eightHours": "8 hours", 92 | "container.browserIntegrityCheckCard.twelveHours": "12 hours", 93 | "container.browserIntegrityCheckCard.sixteenHours": "16 hours", 94 | "container.browserIntegrityCheckCard.twentyHours": "20 hours", 95 | "container.browserIntegrityCheckCard.oneDay": "1 day", 96 | "container.browserIntegrityCheckCard.twoDays": "2 days", 97 | "container.browserIntegrityCheckCard.threeDays": "3 days", 98 | "container.browserIntegrityCheckCard.fourDays": "4 days", 99 | "container.browserIntegrityCheckCard.fiveDays": "5 days", 100 | "container.browserIntegrityCheckCard.eightDays": "8 days", 101 | "container.browserIntegrityCheckCard.sixteenDays": "16 days", 102 | "container.browserIntegrityCheckCard.twentyFourDays": "24 days", 103 | "container.browserIntegrityCheckCard.oneMonth": "1 month", 104 | "container.browserIntegrityCheckCard.twoMonths": "2 months", 105 | "container.browserIntegrityCheckCard.sixMonths": "6 months", 106 | "container.browserIntegrityCheckCard.oneYear": "1 year", 107 | "container.bypassCacheByCookieCard.title": "Cache HTML at CDN", 108 | "container.bypassCacheByCookieCard.description": "Bypass Cache By Cookie allows caching static content such as HTML improving performance of the website.", 109 | "container.bypassCacheByCookieCard.button": "Learn More", 110 | "container.cacheLevelCard.title": "Caching Level", 111 | "container.cacheLevelCard.description": "Determine how much of your website's static content you want Cloudflare to cache. Increased caching can speed up page load time.", 112 | "container.cacheLevelCard.simplified": "No Query String", 113 | "container.cacheLevelCard.basic": "Ignore Query String", 114 | "container.cacheLevelCard.aggressive": "Standard", 115 | "container.challengePassageCard.description": "Specify how long a visitor is allowed access to your website after completing a challenge.", 116 | "container.challengePassageCard.select.fiveMinutes": "5 Minutes", 117 | "container.challengePassageCard.select.fifteenMinutes": "15 Minutes", 118 | "container.challengePassageCard.select.thirtyMinutes": "30 Minutes", 119 | "container.challengePassageCard.select.fortyFiveMinutes": "45 Minutes", 120 | "container.challengePassageCard.select.oneHour": "1 Hour", 121 | "container.challengePassageCard.select.twoHours": "2 Hours", 122 | "container.challengePassageCard.select.threeHours": "3 Hours", 123 | "container.challengePassageCard.select.fourHours": "4 Hours", 124 | "container.challengePassageCard.select.eightHours": "8 Hours", 125 | "container.challengePassageCard.select.sixteenHours": "16 Hours", 126 | "container.challengePassageCard.select.oneDay": "1 Day", 127 | "container.challengePassageCard.select.oneWeek": "1 Week", 128 | "container.challengePassageCard.select.oneMonth": "1 Month", 129 | "container.challengePassageCard.select.oneYear": "1 Year", 130 | "container.challengePassageCard.title": "Challenge Passage", 131 | "container.developmentModeCard.title": "Development Mode", 132 | "container.developmentModeCard.description": "Temporarily bypass our cache allowing you to see changes to your origin server in realtime.", 133 | "container.dnsManagementPage.thead.domain": "Domain", 134 | "container.dnsManagementPage.thead.cloudflarePlan": "Cloudflare Plan", 135 | "container.dnsManagementPage.thead.zoneType": "Zone Type", 136 | "container.dnsManagementPage.thead.status": "Use Cloudflare", 137 | "container.dnsManagementPage.title": "Domain Overview", 138 | "container.dnsRecordEditor.thead.type": "Type", 139 | "container.dnsRecordEditor.thead.name": "Name", 140 | "container.dnsRecordEditor.thead.value": "Value", 141 | "container.dnsRecordEditor.thead.ttl": "TTL", 142 | "container.dnsRecordEditor.thead.status": "Use Cloudflare", 143 | "container.dnsManagementPage.thead.changePlan": "Change", 144 | "container.ipv6Card.title": "IPv6 Compatibility", 145 | "container.ipv6Card.description": "Enable IPv6 support and gateway.", 146 | "container.homePage.title": "Home", 147 | "container.minifyCard.title": "Auto Minify", 148 | "container.minifyCard.description": "Reduce the file size of source code on your website.", 149 | "container.minifyCard.css": "CSS", 150 | "container.minifyCard.html": "HTML", 151 | "container.minifyCard.javascript": "JavaScript", 152 | "container.moresettings.performance": "Performance", 153 | "container.moresettings.speed": "Speed", 154 | "container.moresettings.security": "Security", 155 | "container.pluginSpecificCacheCard.title": "Automatic Cache Management", 156 | "container.pluginSpecificCacheCard.description": "Purge Cloudflare cache automatically when you update the appearance of your site.", 157 | "container.pluginSpecificCacheCard.modal.title": "Confirm Enabling Automatic Cache", 158 | "container.pluginSpecificCacheCard.modal.description": "Enabling this feature will automatically trigger a full cache purge when you update the appearance of your site.", 159 | "container.pluginSpecificCacheCard.modal.button": "I'm sure", 160 | "container.pluginSpecificCacheCard.modal.buttonCancel": "Cancel", 161 | "container.pluginSpecificCacheTagCard.title": "Automatically Purge Cache When Site Content is Updated", 162 | "container.pluginSpecificCacheTagCard.description": "Enable to automatically purge the cache when your site content changes.", 163 | "container.automatichttprewrites.title": "Automatic HTTPS Rewrites", 164 | "container.automatichttprewrites.description": "Automatic HTTPS Rewrites helps fix mixed content by changing \"http\" to \"https\" for all resources or links on your web site that can be served with HTTPS.", 165 | "container.purgeCacheCard.dropdown": "Purge Cache", 166 | "container.purgeCacheCard.button": "Purge Everything", 167 | "container.purgeCacheCard.title": "Purge Cache", 168 | "container.purgeCacheCard.description": "Clear cached files to force Cloudflare to fetch a fresh version of those files from your web server.", 169 | "container.purgeCacheCard.success": "Cache was successfully purged.", 170 | "container.purgeCacheCard.modal.title" : "Confirm Purge All", 171 | "container.purgeCacheCard.modal.description": "Note: Purging your cache may slow your website temporarily.", 172 | "container.purgeCacheCard.modal.buttonCancel": "Cancel", 173 | "container.purgeCacheByURLCard.button": "Purge Individual Files", 174 | "container.purgeCacheByURLCard.success": "Successfully purged all assets. Please allow up to 30 seconds for changes to take effect.", 175 | "container.purgeCacheByURLCard.modal.title" : "Confirm Individual Files", 176 | "container.purgeCacheByURLCard.modal.description": "You can purge up to 30 files at a time. Note: Wildcards are not supported with single file purge at this time. You will need to specify the full path to the file. Separate URL(s) with spaces, or list one per line", 177 | "container.purgeCacheByURLCard.modal.buttonCancel": "Cancel", 178 | "container.railgunCard.title":"Railgun™", 179 | "container.railgunCard.description":"Accelerate delivery of dynamic content. Note: Requires software installation at your host.", 180 | "container.railgunCard.table.name":"Name", 181 | "container.railgunCard.table.railgunState":"Railgun State", 182 | "container.railgunCard.table.connectedToWebsite":"Connected to Website", 183 | "container.railgunCard.table.active": "Active", 184 | "container.railgunCard.table.inactive": "Inactive", 185 | "container.railgunCard.noRailgunsAvailable": "No Railguns are currently configured from your hosting provider for {zoneName}.", 186 | "container.signup.error.emailBlank": "Your Email address cannot be blank.", 187 | "container.signup.error.passwordBlank": "Your password can not be blank.", 188 | "container.signup.error.passwordsDontMatch": "The passwords you entered do not match.", 189 | "container.signup.error.termsOfService": "Please agree to the Terms of Service and Privacy Policy before proceeding.", 190 | "container.signup.form.title": "Sign Up for Cloudflare", 191 | "container.signup.form.email": "Email", 192 | "container.signup.form.password": "Password", 193 | "container.signup.form.passwordAgain": "Password (again)", 194 | "container.signup.form.button": "Sign Up for Cloudflare", 195 | "container.signup.form.termsAndConditions.iAgreeTo": "I agree to ", 196 | "container.signup.form.termsAndConditions.cloudFlaresTermsAndConditions": " Cloudflare's terms and conditions", 197 | "container.signup.form.termsAndConditions.and": "and", 198 | "container.signup.form.termsAndConditions.privacyPolicy": "privacy policy", 199 | "container.signup.form.termsAndConditions.period": ".", 200 | "container.securityLevelCard.description": "Adjust your website's Security Level to determine which visitors will receive a challenge page.", 201 | "container.securityLevelCard.select.essentiallyOff": "Essentially Off", 202 | "container.securityLevelCard.select.low": "Low", 203 | "container.securityLevelCard.select.medium": "Medium", 204 | "container.securityLevelCard.select.high": "High", 205 | "container.securityLevelCard.select.underAttack": "Under Attack", 206 | "container.securityLevelCard.title": "Security Level", 207 | "container.sslCard.description": "Encrypt communication to and from your website using SSL.", 208 | "container.sslCard.select.off": "Off", 209 | "container.sslCard.select.flexible": "Flexible", 210 | "container.sslCard.select.full": "Full", 211 | "container.sslCard.select.full_strict": "Full Strict", 212 | "container.sslCard.title": "SSL", 213 | "container.underAttackButton.description": "Is your website currently being attacked?", 214 | "container.underAttackButton.turnOff": "Enable \"I'm Under Attack\" Mode", 215 | "container.underAttackButton.turnOn": "Disable \"I'm Under Attack\" Mode", 216 | "container.zoneProvision.button.full": "Provision Domain with Full Zone Setup", 217 | "container.zoneProvision.button.cname": "Provision Domain with CNAME Setup", 218 | "container.zoneProvision.button.deprovision": "Remove domain from Cloudflare", 219 | "container.zoneProvision.provisionDifference": "What is the difference between Full and Cname provisioning?", 220 | "container.zoneProvision.title": "Provision Domain", 221 | "container.zoneProvision.modal.title": "Delete Website", 222 | "container.zoneProvision.modal.description": "Are you sure you want to delete {zoneName}? To temporarily disable Cloudflare service and save your website settings, pause your website instead.", 223 | "container.zoneProvision.modal.buttonCancel": "Cancel", 224 | "container.ipRewrite.title": "IP Rewrite", 225 | "container.ipRewrite.description": "Rewrite Cloudflare IP Addresses for actual end-user IP Addresses at the application layer.", 226 | "container.imageOptimization.title": "Image Optimization", 227 | "container.imageOptimization.description": "Improve image load time and for pages that include images on mobile devices with slow network connections", 228 | "container.waf.title": "Web Application Firewall", 229 | "container.waf.description": "Set rules to protect your website from web vulnerabilities.", 230 | "constants.plans.pro": "Pro plan", 231 | "constants.plans.biz": "Business plan", 232 | "constants.plans.ent": "Enterprise plan", 233 | "errors.noActiveZoneSelected": "Please select a domain that is provisioned with Cloudflare.", 234 | "warning.usingSubdomain": "You are using a subdomain for your site, but any Cloudflare settings applied via this plugin will be applied to your original domain as well.", 235 | "warning.developmentmode": "Development mode enabled, all traffic will bypass the Cloudflare cache.", 236 | "utils.utils.lastmodifieddate": "This settings was last changed {date}" 237 | } 238 | --------------------------------------------------------------------------------