├── LICENSE ├── assets ├── cookie-policy.css ├── cookie-policy.js ├── cookie-policy.min.css └── cookie-policy.min.js ├── composer.json ├── gm-cookie-policy.php ├── lang ├── gm-cookie-policy-it_IT.mo ├── gm-cookie-policy-ro_RO.mo └── gm-cookie-policy.pot ├── src ├── Assets.php ├── Config.php ├── Controller.php ├── Cookie.php ├── Message.php ├── RendererInterface.php ├── SettingsPage.php └── SimpleRenderer.php ├── templates ├── message.php └── settings.php └── uninstall.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Giuseppe Mazzapica 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /assets/cookie-policy.css: -------------------------------------------------------------------------------- 1 | #gm-cookie-policy { 2 | width: 100%; 3 | position: fixed; 4 | left: 0; 5 | z-index: 999; 6 | } 7 | 8 | #gm-cookie-policy.cookie-policy-header { 9 | top: 0; 10 | padding-top: 10px; 11 | } 12 | 13 | #gm-cookie-policy.cookie-policy-footer { 14 | bottom: 0; 15 | padding-bottom: 10px; 16 | } 17 | 18 | #gm-cookie-policy .cookie-policy-msg { 19 | margin: auto; 20 | max-width: 760px; 21 | padding: 10px; 22 | } -------------------------------------------------------------------------------- /assets/cookie-policy.js: -------------------------------------------------------------------------------- 1 | var cookiePolicy = window.cookiePolicy || {}; 2 | 3 | (function ($, document, CP) { 4 | 5 | "use strict"; 6 | 7 | CP.cookie = CP.cookie || {}; 8 | CP.cookie.name = CP.cookie.name || ''; 9 | CP.cookie.expire = CP.cookie.expire || 0; 10 | CP.accepted = CP.accepted || false; 11 | 12 | 13 | // When document is loaded we set `window.cookiePolicy.accepted` to proper value so it can be 14 | // checked but other scripts. 15 | $(document).ready(function () { 16 | var event = $.Event('cookie-policy-loaded'); 17 | CP.accepted = document.cookie.indexOf(CP.cookie.name) > -1; 18 | // Other scripts can listen to this event to check user already accepted cookies. 19 | event.accepted = CP.accepted; 20 | $(document).trigger(event); 21 | console.log('Cookie policy loaded.'); 22 | }); 23 | 24 | 25 | // When user close the message, we hide it, set the cookie and fire a custom event 26 | $(document).on('click', '#cookie-policy-close', function (e) { 27 | e.preventDefault(); 28 | e.stopImmediatePropagation(); 29 | 30 | var d = new Date(), now = d.getTime(), value = Math.floor(now / 1000); 31 | d.setTime(now + (CP.cookie.expire * 1000)); 32 | 33 | // Hide message 34 | $('#gm-cookie-policy').hide(); 35 | 36 | // Set cookie 37 | document.cookie = CP.cookie.name + '=' + value + '; expires=' + d.toUTCString() + '; path=/'; 38 | 39 | // Fire event that can be used to initialize some features only when user accepted cookies. 40 | var event = $.Event('cookie-policy-accepted'); 41 | event.cookie = CP.cookie; 42 | $(document).trigger(event); 43 | 44 | return false; 45 | }); 46 | 47 | 48 | // When user accepts cookie policy, we update `window.cookiePolicy.accepted` var accordingly 49 | $(document).on('cookie-policy-accepted', function () { 50 | CP.accepted = true; 51 | console.log('Cookie policy accepted.'); 52 | }); 53 | 54 | })(jQuery, document, cookiePolicy); 55 | -------------------------------------------------------------------------------- /assets/cookie-policy.min.css: -------------------------------------------------------------------------------- 1 | #gm-cookie-policy{width:100%;position:fixed;left:0;z-index:999}#gm-cookie-policy.cookie-policy-header{top:0;padding-top:10px}#gm-cookie-policy.cookie-policy-footer{bottom:0;padding-bottom:10px}#gm-cookie-policy .cookie-policy-msg{margin:auto;max-width:760px;padding:10px} -------------------------------------------------------------------------------- /assets/cookie-policy.min.js: -------------------------------------------------------------------------------- 1 | var cookiePolicy=window.cookiePolicy||{};(function(b,a,c){c.cookie=c.cookie||{};c.cookie.name=c.cookie.name||"";c.cookie.expire=c.cookie.expire||0;c.accepted=c.accepted||false;b(a).ready(function(){var d=b.Event("cookie-policy-loaded");c.accepted=a.cookie.indexOf(c.cookie.name)>-1;d.accepted=c.accepted;b(a).trigger(d);console.log("Cookie policy loaded.")});b(a).on("click","#cookie-policy-close",function(i){i.preventDefault();i.stopImmediatePropagation();var j=new Date(),f=j.getTime(),h=Math.floor(f/1000);j.setTime(f+(c.cookie.expire*1000));b("#gm-cookie-policy").hide();a.cookie=c.cookie.name+"="+h+"; expires="+j.toUTCString()+"; path=/";var g=b.Event("cookie-policy-accepted");g.cookie=c.cookie;b(a).trigger(g);return false});b(a).on("cookie-policy-accepted",function(){c.accepted=true;console.log("Cookie policy accepted.")})})(jQuery,document,cookiePolicy); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gmazzap/gm-cookie-policy", 3 | "license": "MIT", 4 | "description": "A simple plugin to show a message for compliance with EU cookie law.", 5 | "keywords": [ 6 | "wordpress", 7 | "cookie", 8 | "cookie policy" 9 | ], 10 | "type": "wordpress-plugin", 11 | "minimum-stability": "stable", 12 | "require": { 13 | "php": ">=5.4", 14 | "composer/installers": "^1.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "GM\\CookiePolicy\\": "src/" 19 | } 20 | }, 21 | "config": { 22 | "optimize-autoloader": true 23 | } 24 | } -------------------------------------------------------------------------------- /gm-cookie-policy.php: -------------------------------------------------------------------------------- 1 | 14 | * 15 | * For the full copyright and license information, please view the LICENSE 16 | * file that was distributed with this source code. 17 | */ 18 | 19 | namespace GM\CookiePolicy; 20 | 21 | /** 22 | * Load Composer autoload if available, otherwise register a simple autoload callback. 23 | * 24 | * @return void 25 | */ 26 | function autoload() 27 | { 28 | static $done; 29 | if (! $done && ! class_exists('GM\CookiePolicy\Config', true)) { 30 | $done = true; 31 | file_exists(__DIR__.'/vendor/autoload.php') 32 | ? require_once __DIR__.'/vendor/autoload.php' 33 | : spl_autoload_register(function ($class) { 34 | if (strpos($class, __NAMESPACE__) === 0) { 35 | $name = str_replace('\\', '/', substr($class, strlen(__NAMESPACE__))); 36 | /** @noinspection PhpIncludeInspection */ 37 | require_once __DIR__."/src{$name}.php"; 38 | } 39 | }); 40 | } 41 | } 42 | 43 | // A filter that can be used to check if the user accepted cookies or not. 44 | // Being a filter, checks can be done without relying on specific plugin class or functions. 45 | add_filter('cookie-policy-accepted', function () { 46 | autoload(); 47 | return (new Cookie())->exists(); 48 | }); 49 | 50 | // Nothing more to do on AJAX requests 51 | (defined('DOING_AJAX') && DOING_AJAX) or add_action('wp_loaded', function () { 52 | 53 | autoload(); 54 | 55 | // Controller class is responsible to instantiate objects and attach their methods to proper hooks. 56 | $controller = new Controller(); 57 | 58 | // User accepted policy, let's save a cookie to don't show message again and then return. 59 | // This is needed to ensure plugin works when js is disabled so cookie have to be set in PHP. 60 | $accepted = $controller->maybePolicyAccepted(); 61 | if ($accepted) { 62 | return; 63 | } 64 | 65 | // Instantiate config class 66 | $config = new Config( 67 | [ 68 | 'plugin-path' => __FILE__, 69 | 'no-cookie-url' => esc_url(add_query_arg([Cookie::ACCEPTED_QUERY => time()])), 70 | ], 71 | SettingsPage::defaults() 72 | ); 73 | 74 | // Setup backend actions 75 | $controller->setupBackendActions($config); 76 | 77 | // Setup frontend action 78 | $controller->setupFrontendActions($config); 79 | }); 80 | -------------------------------------------------------------------------------- /lang/gm-cookie-policy-it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmazzap/gm-cookie-policy/a89268487577c70d3b320e630011a2622073b0ac/lang/gm-cookie-policy-it_IT.mo -------------------------------------------------------------------------------- /lang/gm-cookie-policy-ro_RO.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmazzap/gm-cookie-policy/a89268487577c70d3b320e630011a2622073b0ac/lang/gm-cookie-policy-ro_RO.mo -------------------------------------------------------------------------------- /lang/gm-cookie-policy.pot: -------------------------------------------------------------------------------- 1 | # EU Cookie Policy 2 | # Copyright (C) 2016 Giuseppe Mazzapica 3 | # This file is distributed under MIT license. 4 | #, fuzzy 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: gm-cookie-policy\n" 8 | "POT-Creation-Date: 2016-04-20 18:30+0100\n" 9 | "PO-Revision-Date: 2016-04-20 18:30+0100\n" 10 | "Language: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "Plural-Forms: plurals=2; plural=(n > 1)\n" 15 | 16 | #: src/SettingsPage.php:51 17 | msgctxt "policy message link text" 18 | msgid "Close." 19 | msgstr "" 20 | 21 | #: src/SettingsPage.php:53 22 | msgctxt "policy message link text" 23 | msgid "Read more." 24 | msgstr "" 25 | 26 | #: src/SettingsPage.php:87 27 | msgctxt "setting title" 28 | msgid "Cookie Policy Settings" 29 | msgstr "" 30 | 31 | #: src/SettingsPage.php:88 32 | msgid "Cookie Policy" 33 | msgstr "" 34 | 35 | #: src/SettingsPage.php:185 36 | msgctxt "form label" 37 | msgid "Enable" 38 | msgstr "" 39 | 40 | #: src/SettingsPage.php:186 41 | msgctxt "form label" 42 | msgid "Message" 43 | msgstr "" 44 | 45 | #: src/SettingsPage.php:187 46 | msgctxt "form label" 47 | msgid "Use Styles" 48 | msgstr "" 49 | 50 | #: src/SettingsPage.php:188 51 | msgctxt "form label" 52 | msgid "Use Script" 53 | msgstr "" 54 | 55 | #: src/SettingsPage.php:189 56 | msgctxt "form label" 57 | msgid "Background Color" 58 | msgstr "" 59 | 60 | #: src/SettingsPage.php:190 61 | msgctxt "form label" 62 | msgid "Link Color" 63 | msgstr "" 64 | 65 | #: src/SettingsPage.php:191 66 | msgctxt "form label" 67 | msgid "Text Color" 68 | msgstr "" 69 | 70 | #: src/SettingsPage.php:192 71 | msgctxt "form label" 72 | msgid "Position" 73 | msgstr "" 74 | 75 | #: src/SettingsPage.php:193 76 | msgctxt "form label" 77 | msgid "Header" 78 | msgstr "" 79 | 80 | #: src/SettingsPage.php:194 81 | msgctxt "form label" 82 | msgid "Footer" 83 | msgstr "" 84 | 85 | #: src/SettingsPage.php:195 86 | msgctxt "form label" 87 | msgid "\"Close\" Link Text" 88 | msgstr "" 89 | 90 | #: src/SettingsPage.php:196 91 | msgctxt "form label" 92 | msgid "\"Read More\" URL" 93 | msgstr "" 94 | 95 | #: src/SettingsPage.php:197 96 | msgctxt "form label" 97 | msgid "\"Read More\" Link Text" 98 | msgstr "" 99 | 100 | #: src/SettingsPage.php:277 101 | msgid "User not allowed." 102 | msgstr "" 103 | 104 | #: src/SettingsPage.php:278 105 | msgid "An error occurred while saving configuration." 106 | msgstr "" 107 | 108 | #: templates/settings.php:17 109 | msgctxt "form label" 110 | msgid "Enable the message on frontend?" 111 | msgstr "" 112 | 113 | #: templates/settings.php:48 114 | msgctxt "form label" 115 | msgid "Shortcodes allowed." 116 | msgstr "" 117 | 118 | #: templates/settings.php:100 templates/settings.php:121 119 | msgctxt "form desc" 120 | msgid "Leave empty for no \"Read More\" link." 121 | msgstr "" 122 | 123 | #: templates/settings.php:185 124 | msgctxt "settings title" 125 | msgid "Colors Settings" 126 | msgstr "" 127 | 128 | #: templates/settings.php:188 129 | msgctxt "settings desc" 130 | msgid "Ignored if \"Use Styles\" is disabled above." 131 | msgstr "" -------------------------------------------------------------------------------- /src/Assets.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | class Assets 19 | { 20 | const HANDLE = 'cookie-policy'; 21 | 22 | /** 23 | * @var array 24 | */ 25 | private $info; 26 | 27 | /** 28 | * @var array 29 | */ 30 | private $use; 31 | 32 | /** 33 | * @var array 34 | */ 35 | private $colors; 36 | 37 | /** 38 | * Assets constructor. 39 | * 40 | * @param \GM\CookiePolicy\Config $config 41 | */ 42 | public function __construct(Config $config) 43 | { 44 | $debug = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG; 45 | $name = '/assets/cookie-policy'; 46 | $debug and $name .= '.min'; 47 | $path = dirname($config['plugin-path']).$name; 48 | $this->info = compact('name', 'path', 'debug'); 49 | $this->info['plugin'] = $config['plugin-path']; 50 | $this->info['position'] = $config['show-on']; 51 | $this->use = ['scripts' => $config['use-script'], 'styles' => $config['use-style']]; 52 | $this->colors = [ 53 | 'txt' => $config['txt-color'], 54 | 'bg' => $config['bg-color'], 55 | 'link' => $config['link-color'] 56 | ]; 57 | } 58 | 59 | /** 60 | * @return bool 61 | */ 62 | public function setupApiScripts() 63 | { 64 | return $this->shouldUse('scripts-api') ? $this->setupScripts() : false; 65 | } 66 | 67 | /** 68 | * @return bool 69 | */ 70 | public function setupScripts() 71 | { 72 | if ($this->shouldUse('scripts')) { 73 | wp_enqueue_script( 74 | self::HANDLE, 75 | plugins_url($this->info['name'].'.js', $this->info['plugin']), 76 | ['jquery'], 77 | $this->info['debug'] ? time() : @filemtime($this->info['path'].'.js'), 78 | true 79 | ); 80 | 81 | wp_localize_script(self::HANDLE, 'cookiePolicy', [ 82 | 'accepted' => false, 83 | 'cookie' => ['name' => Cookie::COOKIE, 'expire' => Cookie::expiration()] 84 | ]); 85 | 86 | return true; 87 | } 88 | 89 | return false; 90 | } 91 | 92 | /** 93 | * @return bool 94 | */ 95 | public function setupStyles() 96 | { 97 | if ($this->shouldUse('styles')) { 98 | wp_enqueue_style( 99 | self::HANDLE, 100 | plugins_url($this->info['name'].'.css', $this->info['plugin']), 101 | [], 102 | $this->info['debug'] ? time() : @filemtime($this->info['path'].'.css'), 103 | 'screen' 104 | ); 105 | 106 | wp_add_inline_style(self::HANDLE, $this->inlineStyle()); 107 | 108 | return true; 109 | } 110 | 111 | return false; 112 | } 113 | 114 | /** 115 | * @param string $which 116 | * @return bool 117 | */ 118 | private function shouldUse($which) 119 | { 120 | $key = explode('-', $which); 121 | $use = filter_var($this->use[$key[0]], FILTER_VALIDATE_BOOLEAN); 122 | 123 | return (bool) apply_filters("cookie-policy.use-{$which}", $use); 124 | } 125 | 126 | /** 127 | * @return mixed 128 | */ 129 | private function inlineStyle() 130 | { 131 | $border = $this->info['position'] === 'header' ? 'bottom' : 'top'; 132 | ob_start(); 133 | ?> 134 | #gm-cookie-policy { 135 | background-color: colors['bg']) ?>; 136 | color: colors['txt']) ?>; 137 | border-: colors['link']) ?> 1px solid; 138 | } 139 | #gm-cookie-policy p, 140 | #gm-cookie-policy .cookie-policy-msg, 141 | #gm-cookie-policy .cookie-policy-msg p, 142 | #gm-cookie-policy .cookie-policy-msg table, 143 | #gm-cookie-policy .cookie-policy-msg table p { 144 | color: colors['txt']) ?>; 145 | } 146 | #gm-cookie-policy a, 147 | #gm-cookie-policy .cookie-policy-msg a, 148 | #gm-cookie-policy .cookie-policy-msg table a { 149 | color: colors['link']) ?>; 150 | } 151 | colors, $this->info); 154 | 155 | return apply_filters('cookie-policy.message-inline-css', $css, $context); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace GM\CookiePolicy; 11 | 12 | /** 13 | * @author Giuseppe Mazzapica 14 | * @license http://opensource.org/licenses/MIT MIT 15 | * @package gm-cookie-policy 16 | */ 17 | class Config implements \ArrayAccess 18 | { 19 | const OPTION = 'gm-cookie-policy'; 20 | const CAP = 'manage_options'; 21 | 22 | /** 23 | * @var \ArrayObject 24 | */ 25 | private $stored; 26 | 27 | /** 28 | * @var \ArrayObject 29 | */ 30 | private $live; 31 | 32 | /** 33 | * @param \GM\CookiePolicy\Config $config 34 | * @param array $newData 35 | * @return \GM\CookiePolicy\Config 36 | */ 37 | public static function newInstanceFrom(Config $config = null, array $newData = []) 38 | { 39 | $live = $config ? $config->live->getArrayCopy() : []; 40 | $stored = $config ? array_merge($config->stored->getArrayCopy(), $newData) : $newData; 41 | $instance = new static($live, []); 42 | $instance->stored = new \ArrayObject($stored); 43 | 44 | return $instance; 45 | } 46 | 47 | /** 48 | * @param array $liveConfig 49 | * @param array $defaults 50 | */ 51 | public function __construct(array $liveConfig = [], array $defaults = []) 52 | { 53 | $stored = get_option(self::OPTION) ?: []; 54 | $this->stored = new \ArrayObject(array_merge($defaults, $stored)); 55 | $liveConfig['capability'] = apply_filters('cookie-policy.config-capability', self::CAP); 56 | $this->live = new \ArrayObject($liveConfig); 57 | } 58 | 59 | /** 60 | * @return array 61 | */ 62 | public function toArray() 63 | { 64 | return array_merge($this->stored->getArrayCopy(), $this->live->getArrayCopy()); 65 | } 66 | 67 | /** 68 | * @return bool 69 | */ 70 | public function save() 71 | { 72 | if (! is_admin() || ! current_user_can($this->live['capability'])) { 73 | return false; 74 | } 75 | 76 | $now = get_option(self::OPTION, []); 77 | if ($now === $this->stored->getArrayCopy()) { 78 | return true; 79 | } 80 | 81 | return update_option(self::OPTION, $this->stored->getArrayCopy(), 'no'); 82 | } 83 | 84 | /** 85 | * @inheritdoc 86 | */ 87 | public function offsetExists($offset) 88 | { 89 | return $this->stored->offsetExists($offset) || $this->live->offsetExists($offset); 90 | } 91 | 92 | /** 93 | * @inheritdoc 94 | */ 95 | public function offsetGet($offset) 96 | { 97 | if (! $this->offsetExists($offset)) { 98 | throw new \BadMethodCallException($offset.' is not a valid config entry.'); 99 | } 100 | 101 | return $this->stored->offsetExists($offset) 102 | ? $this->stored->offsetGet($offset) 103 | : $this->live->offsetGet($offset); 104 | } 105 | 106 | /** 107 | * @inheritdoc 108 | */ 109 | public function offsetSet($offset, $value) 110 | { 111 | if ($this->offsetExists($offset)) { 112 | throw new \BadMethodCallException('Values in '.__CLASS__.' can\'t be modified.'); 113 | } 114 | 115 | $this->stored->offsetSet($offset, $value); 116 | } 117 | 118 | /** 119 | * @inheritdoc 120 | */ 121 | public function offsetUnset($offset) 122 | { 123 | throw new \BadMethodCallException('Values in '.__CLASS__.' can\'t be modified.'); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Controller.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | class Controller 19 | { 20 | /** 21 | * @var bool 22 | */ 23 | private $isAdmin; 24 | 25 | /** 26 | * Controller constructor. 27 | */ 28 | public function __construct() 29 | { 30 | $this->isAdmin = is_admin(); 31 | } 32 | 33 | /** 34 | * When JS is disabled cookie have to be set in PHP. 35 | * This methods looks at query string to see if user accepted policy and set cookie if necessary. 36 | * 37 | * @param \GM\CookiePolicy\Cookie $cookie 38 | * @return bool 39 | */ 40 | public function maybePolicyAccepted(Cookie $cookie = null) 41 | { 42 | if ( 43 | ! $this->isAdmin 44 | && filter_input(INPUT_GET, Cookie::ACCEPTED_QUERY, FILTER_VALIDATE_INT) > 0 45 | ) { 46 | $cookie or $cookie = new Cookie(); 47 | $cookie->save(); 48 | 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | /** 56 | * Setup backend hooks. 57 | * Instantiate necessary objects if necessary. 58 | * 59 | * @param \GM\CookiePolicy\Config $config 60 | * @param \GM\CookiePolicy\SettingsPage|null $settings 61 | * @param \GM\CookiePolicy\RendererInterface|null $renderer 62 | */ 63 | public function setupBackendActions( 64 | Config $config, 65 | SettingsPage $settings = null, 66 | RendererInterface $renderer = null 67 | ) { 68 | if (! $this->isAdmin) { 69 | return; 70 | } 71 | 72 | $this->loadTextDomain(); 73 | 74 | // Setup settings page 75 | add_action('admin_menu', function () use ($config, $settings, $renderer) { 76 | $renderer or $renderer = new SimpleRenderer(); 77 | $settings or $settings = new SettingsPage($config, $renderer); 78 | $settings->setup(); 79 | }); 80 | 81 | // Save setting page form when submitted 82 | add_action('admin_post_'.SettingsPage::ACTION, 83 | function () use ($config, $settings, $renderer) { 84 | $renderer or $renderer = new SimpleRenderer(); 85 | $settings or $settings = new SettingsPage($config, $renderer); 86 | $settings->save(); 87 | exit(); 88 | }); 89 | } 90 | 91 | /** 92 | * Setup frontend hooks. 93 | * Instantiate necessary objects if necessary. 94 | * 95 | * @param \GM\CookiePolicy\Config $config 96 | * @param \GM\CookiePolicy\Cookie|null $cookie 97 | * @param \GM\CookiePolicy\Message|null $message 98 | * @param \GM\CookiePolicy\Assets $assets 99 | */ 100 | public function setupFrontendActions( 101 | Config $config, 102 | Cookie $cookie = null, 103 | Message $message = null, 104 | Assets $assets = null 105 | ) { 106 | if ($this->isAdmin || ! filter_var($config['enabled'], FILTER_VALIDATE_BOOLEAN)) { 107 | return; 108 | } 109 | 110 | $this->loadTextDomain(); 111 | 112 | add_action('template_redirect', function () use ($config, $cookie, $message, $assets) { 113 | $cookie or $cookie = new Cookie(); 114 | $assets or $assets = new Assets($config); 115 | $exists = $cookie->exists(); 116 | /** @var callable $setupScripts */ 117 | $setupScripts = $exists ? [$assets, 'setupApiScripts'] : [$assets, 'setupScripts']; 118 | 119 | // Add necessary script according to config 120 | add_action('wp_enqueue_scripts', $setupScripts); 121 | 122 | // If the cookie exists, user accepted policy, nothing else to do 123 | if ($exists) { 124 | return; 125 | } 126 | 127 | // Avoid show message when in the "Read More" page 128 | $moreUrl = rtrim($config['more-url'], '/'); 129 | if (!$moreUrl || $moreUrl !== rtrim(esc_url(home_url(add_query_arg([]))), '/')) { 130 | $message or $message = new Message($config, $this->createRenderer($config)); 131 | 132 | // Add necessary assets according to config 133 | add_action('wp_enqueue_scripts', [$assets, 'setupStyles']); 134 | // Render message content 135 | add_action('wp_footer', [$message, 'render']); 136 | } 137 | }); 138 | } 139 | 140 | /** 141 | * This factory gives users possibility to use a different renderer object 142 | * for the rendering of message. 143 | * 144 | * @param \GM\CookiePolicy\Config $config 145 | * @return \GM\CookiePolicy\RendererInterface 146 | */ 147 | private function createRenderer(Config $config) 148 | { 149 | $renderer = apply_filters('cookie-policy.renderer-instance', null, $config); 150 | $renderer instanceof RendererInterface or $renderer = new SimpleRenderer(); 151 | 152 | return $renderer; 153 | } 154 | 155 | /** 156 | * Load text domain. 157 | */ 158 | private function loadTextDomain() 159 | { 160 | // Load text domain 161 | $pathArr = explode(DIRECTORY_SEPARATOR, dirname(__DIR__)); 162 | load_plugin_textdomain('gm-cookie-policy', false, end($pathArr).'/lang'); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/Cookie.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | class Cookie 19 | { 20 | const COOKIE = 'gm-cookie-policy'; 21 | const ACCEPTED_QUERY = 'cookie-policy-accepted'; 22 | 23 | /** 24 | * @return mixed 25 | */ 26 | public static function expiration() 27 | { 28 | return apply_filters('cookie-policy.cookie-expiration', 3 * MONTH_IN_SECONDS); 29 | } 30 | 31 | /** 32 | * @return bool 33 | */ 34 | public function exists() 35 | { 36 | return filter_input(INPUT_COOKIE, self::COOKIE, FILTER_SANITIZE_NUMBER_INT) > 0; 37 | } 38 | 39 | /** 40 | * @return bool 41 | */ 42 | public function save() 43 | { 44 | if (headers_sent()) { 45 | return false; 46 | } 47 | 48 | $now = time(); 49 | $url = parse_url(home_url()); 50 | $host = isset($url['host']) ? $url['host'] : null; 51 | 52 | return setcookie( 53 | self::COOKIE, 54 | $now, 55 | $now + self::expiration(), 56 | '/', 57 | $host, 58 | is_ssl(), 59 | true 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | class Message 19 | { 20 | /** 21 | * @var \GM\CookiePolicy\Config 22 | */ 23 | private $config; 24 | 25 | /** 26 | * @var \GM\CookiePolicy\RendererInterface 27 | */ 28 | private $renderer; 29 | 30 | /** 31 | * @param \GM\CookiePolicy\Config $config 32 | * @param \GM\CookiePolicy\RendererInterface $renderer 33 | */ 34 | public function __construct(Config $config, RendererInterface $renderer) 35 | { 36 | $this->config = $config; 37 | $this->renderer = $renderer; 38 | } 39 | 40 | /** 41 | * @return void 42 | */ 43 | public function render() 44 | { 45 | $message = apply_filters( 46 | 'cookie-policy.message-text', 47 | do_shortcode($this->config['message']), 48 | $this->config 49 | ); 50 | 51 | $data = [ 52 | 'message' => $message, 53 | 'closeLabel' => $this->config['close-label'], 54 | 'moreUrl' => $this->config['more-url'], 55 | 'moreLabel' => $this->config['more-label'], 56 | 'closeUrl' => $this->config['no-cookie-url'], 57 | 'bgColor' => $this->config['bg-color'], 58 | 'linkColor' => $this->config['link-color'], 59 | 'txtColor' => $this->config['txt-color'], 60 | 'showOn' => $this->config['show-on'], 61 | ]; 62 | 63 | $template = apply_filters( 64 | 'cookie-policy.message-template', 65 | dirname(__DIR__).'/templates/message.php' 66 | ); 67 | 68 | echo $this->renderer->render($template, $data); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/RendererInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | interface RendererInterface 19 | { 20 | /** 21 | * Render a template with given context. 22 | * 23 | * @param string $template 24 | * @param array $data 25 | * @return string 26 | */ 27 | public function render($template, array $data = []); 28 | } 29 | -------------------------------------------------------------------------------- /src/SettingsPage.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | class SettingsPage 19 | { 20 | const SLUG = 'gm-cookie-policy'; 21 | const ACTION = 'gm-cookie-policy-save'; 22 | const NONCE_ACTION = 'gm-cookie-policy-nonce'; 23 | const NONCE_KEY = '_gm-cookie-policy'; 24 | const ERR_KEY = 'err'; 25 | const EDITOR_ID = 'cookie-policy-message'; 26 | 27 | /** 28 | * @var \GM\CookiePolicy\Config 29 | */ 30 | private $config; 31 | 32 | /** 33 | * @var \GM\CookiePolicy\RendererInterface 34 | */ 35 | private $renderer; 36 | 37 | /** 38 | * @return array 39 | */ 40 | public static function defaults() 41 | { 42 | return [ 43 | 'enabled' => 'no', 44 | 'use-style' => 'yes', 45 | 'use-script' => 'yes', 46 | 'message' => '', 47 | 'bg-color' => '#FFFFFF', 48 | 'link-color' => '#337ab7', 49 | 'txt-color' => '#555555', 50 | 'show-on' => 'footer', 51 | 'close-label' => esc_html_x('Close.', 'policy message link text', 'gm-cookie-policy'), 52 | 'more-url' => '', 53 | 'more-label' => esc_html_x('Read more.', 'policy message link text', 'gm-cookie-policy'), 54 | ]; 55 | } 56 | 57 | /** 58 | * @param \GM\CookiePolicy\Config $config 59 | * @param \GM\CookiePolicy\RendererInterface $renderer 60 | */ 61 | public function __construct(Config $config, RendererInterface $renderer) 62 | { 63 | $this->config = $config; 64 | $this->renderer = $renderer; 65 | } 66 | 67 | /** 68 | * @return \GM\CookiePolicy\SettingsPage 69 | */ 70 | public function setup() 71 | { 72 | if (! current_user_can($this->config['capability'])) { 73 | return; 74 | } 75 | 76 | // Setup assets and markup for settings page 77 | 78 | add_action('admin_enqueue_scripts', function ($page) { 79 | if ($page === 'tools_page_'.self::SLUG) { 80 | wp_enqueue_style('wp-color-picker'); 81 | wp_enqueue_script('wp-color-picker'); 82 | } 83 | }); 84 | 85 | add_submenu_page( 86 | 'tools.php', 87 | esc_html_x('Cookie Policy Settings', 'setting title', 'gm-cookie-policy'), 88 | esc_html__('Cookie Policy', 'gm-cookie-policy'), 89 | $this->config['capability'], 90 | self::SLUG, 91 | function () { 92 | echo $this->renderer->render( 93 | dirname(__DIR__).'/templates/settings.php', 94 | $this->settingsContext() 95 | ); 96 | } 97 | ); 98 | 99 | add_filter('teeny_mce_buttons', function ($buttons, $editor) { 100 | if ($editor === self::EDITOR_ID) { 101 | $buttons = ['bold', 'italic', 'bullist', 'numlist', 'hr', 'link', 'unlink']; 102 | } 103 | 104 | return $buttons; 105 | }, 10, 2); 106 | 107 | // Show error notices if needed 108 | add_action('current_screen', function (\WP_Screen $screen) { 109 | $err = filter_input(INPUT_GET, self::ERR_KEY, FILTER_VALIDATE_INT); 110 | if ($err && ($screen->id === 'tools_page_'.self::SLUG)) { 111 | add_action('admin_notices', function () use ($err) { 112 | $msg = $this->errorMessage($err); 113 | $msg and printf('

%s

', $msg); 114 | }); 115 | } 116 | }); 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * @return bool|\WP_Error 123 | */ 124 | public function save() 125 | { 126 | if (! current_user_can($this->config['capability'])) { 127 | return false; 128 | } 129 | 130 | $url = add_query_arg(['page' => self::SLUG], admin_url('tools.php')); 131 | 132 | if (! current_user_can($this->config['capability'])) { 133 | wp_safe_redirect(add_query_arg(['err' => 10], $url)); 134 | 135 | return false; 136 | } 137 | 138 | $data = $this->inputData(); 139 | if (is_wp_error($data)) { 140 | wp_safe_redirect(add_query_arg(['err' => 10], $url)); 141 | 142 | return false; 143 | } 144 | 145 | $config = Config::newInstanceFrom($this->config, $data); 146 | 147 | if (! $config->save()) { 148 | wp_safe_redirect(add_query_arg(['err' => 20], $url)); 149 | 150 | return false; 151 | } 152 | 153 | wp_safe_redirect($url); 154 | 155 | return true; 156 | } 157 | 158 | /** 159 | * @return array 160 | */ 161 | private function settingsContext() 162 | { 163 | global $title; 164 | 165 | return [ 166 | 'title' => $title, 167 | 'message' => $this->config['message'], 168 | 'actionUrl' => admin_url('admin-post.php'), 169 | 'actionName' => self::ACTION, 170 | 'nonceName' => self::NONCE_KEY, 171 | 'nonceValue' => wp_create_nonce(self::NONCE_ACTION), 172 | 'values' => [ 173 | 'enabled' => $this->config['enabled'], 174 | 'use-style' => $this->config['use-style'], 175 | 'use-script' => $this->config['use-script'], 176 | 'bg-color' => $this->config['bg-color'], 177 | 'link-color' => $this->config['link-color'], 178 | 'txt-color' => $this->config['txt-color'], 179 | 'show-on' => $this->config['show-on'], 180 | 'close-label' => $this->config['close-label'], 181 | 'more-url' => $this->config['more-url'], 182 | 'more-label' => $this->config['more-label'], 183 | ], 184 | 'labels' => [ 185 | 'enabled' => _x('Enable', 'form label', 'gm-cookie-policy'), 186 | 'message' => _x('Message', 'form label', 'gm-cookie-policy'), 187 | 'use-style' => _x('Use Styles', 'form label', 'gm-cookie-policy'), 188 | 'use-script' => _x('Use Script', 'form label', 'gm-cookie-policy'), 189 | 'bg-color' => _x('Background Color', 'form label', 'gm-cookie-policy'), 190 | 'link-color' => _x('Link Color', 'form label', 'gm-cookie-policy'), 191 | 'txt-color' => _x('Text Color', 'form label', 'gm-cookie-policy'), 192 | 'show-on' => _x('Position', 'form label', 'gm-cookie-policy'), 193 | 'header' => _x('Header', 'form label', 'gm-cookie-policy'), 194 | 'footer' => _x('Footer', 'form label', 'gm-cookie-policy'), 195 | 'close-label' => _x('"Close" Link Text', 'form label', 'gm-cookie-policy'), 196 | 'more-url' => _x('"Read More" URL', 'form label', 'gm-cookie-policy'), 197 | 'more-label' => _x('"Read More" Link Text', 'form label', 'gm-cookie-policy'), 198 | ], 199 | 'editorId' => self::EDITOR_ID, 200 | 'editorArgs' => [ 201 | 'wpautop' => false, 202 | 'media_buttons' => false, 203 | 'teeny' => true, 204 | 'quicktags' => true, 205 | 'textarea_rows' => 5 206 | ] 207 | ]; 208 | } 209 | 210 | /** 211 | * @return \WP_Error|array 212 | */ 213 | private function inputData() 214 | { 215 | $color = function ($val) { 216 | $val = (is_string($val) && preg_match('~^#?[ABCDEF0-9]{3,6}$~i', $val)) ? $val : ''; 217 | 218 | return (empty($val) || strpos($val, '#') === 0) ? $val : '#'.$val; 219 | }; 220 | 221 | $yesNo = function ($val) { 222 | $val = is_string($val) ? filter_var($val, FILTER_SANITIZE_STRING) : ''; 223 | 224 | return (empty($val) || in_array($val, ['yes', 'no'], true)) ? $val : 'yes'; 225 | }; 226 | 227 | $showOn = function ($val) { 228 | $val = is_string($val) ? strtolower(filter_var($val, FILTER_SANITIZE_STRING)) : ''; 229 | 230 | return in_array($val, ['footer', 'header'], true) ? $val : $val = 'footer'; 231 | }; 232 | 233 | $message = function ($val) { 234 | is_string($val) or $val = ''; 235 | if ($val && ! current_user_can('unfiltered_html')) { 236 | $allow = array_fill_keys(['br', 'em', 'strong', 'ul', 'ol', 'li'], true); 237 | $allow['a'] = ['href' => true]; 238 | $allow['img'] = ['src' => true, 'width' => true, 'height' => true]; 239 | $val = wp_kses($val, $allow); 240 | } 241 | 242 | return $val; 243 | }; 244 | 245 | $data = filter_input_array(INPUT_POST, [ 246 | self::NONCE_KEY => FILTER_SANITIZE_STRING, 247 | 'enabled' => ['filter' => FILTER_CALLBACK, 'options' => $yesNo], 248 | 'use-style' => ['filter' => FILTER_CALLBACK, 'options' => $yesNo], 249 | 'use-script' => ['filter' => FILTER_CALLBACK, 'options' => $yesNo], 250 | 'close-label' => FILTER_SANITIZE_STRING, 251 | 'more-url' => FILTER_SANITIZE_URL, 252 | 'more-label' => FILTER_SANITIZE_STRING, 253 | self::EDITOR_ID => ['filter' => FILTER_CALLBACK, 'options' => $message], 254 | 'show-on' => ['filter' => FILTER_CALLBACK, 'options' => $showOn], 255 | 'bg-color' => ['filter' => FILTER_CALLBACK, 'options' => $color], 256 | 'link-color' => ['filter' => FILTER_CALLBACK, 'options' => $color], 257 | 'txt-color' => ['filter' => FILTER_CALLBACK, 'options' => $color], 258 | ]); 259 | 260 | if (! wp_verify_nonce($data[self::NONCE_KEY], self::NONCE_ACTION)) { 261 | return new \WP_Error(__CLASS__, 'Invalid data.'); 262 | } 263 | 264 | $data['message'] = $data[self::EDITOR_ID]; 265 | unset($data[self::NONCE_KEY], $data[self::EDITOR_ID]); 266 | 267 | return $data; 268 | } 269 | 270 | /** 271 | * @param int $error 272 | * @return string 273 | */ 274 | private function errorMessage($error) 275 | { 276 | $errors = [ 277 | 10 => esc_html__('User not allowed.', 'gm-cookie-policy'), 278 | 20 => esc_html__('An error occurred while saving configuration.', 'gm-cookie-policy'), 279 | ]; 280 | 281 | return isset($errors[$error]) ? $errors[$error] : ''; 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/SimpleRenderer.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | namespace GM\CookiePolicy; 12 | 13 | /** 14 | * @author Giuseppe Mazzapica 15 | * @license http://opensource.org/licenses/MIT MIT 16 | * @package gm-cookie-policy 17 | */ 18 | final class SimpleRenderer implements RendererInterface 19 | { 20 | /** 21 | * Very simple render engine. Template files can access variables using `$this->variableName`. 22 | * No other variable than the ones passed in `$data` param are available in templates. 23 | * 24 | * @param string $template Full path to template file to render 25 | * @param array $data 26 | * @return string 27 | */ 28 | public function render($template, array $data = []) 29 | { 30 | $context = (object) $data; 31 | 32 | $renderer = \Closure::bind(function ($template) { 33 | ob_start(); 34 | /** @noinspection PhpIncludeInspection */ 35 | @include $template; 36 | 37 | return trim(ob_get_clean()); 38 | }, $context, 'stdClass'); 39 | 40 | return $renderer($template); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /templates/message.php: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /templates/settings.php: -------------------------------------------------------------------------------- 1 |
2 |

title) ?>

3 |
4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 35 | 36 | 37 |
labels['enabled']) ?> 16 |

17 | 18 |

19 | 26 | 27 | 34 |
38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 71 | 72 | 73 | 76 | 84 | 85 | 86 | 87 | 90 | 106 | 107 | 108 | 109 | 112 | 127 | 128 | 129 |
labels['message']) ?> 46 | message, $this->editorId, $this->editorArgs) ?> 47 |

48 | 49 |

50 |
labels['show-on']) ?> 55 | 62 | 63 | 70 |
74 | 75 | 77 | 83 |
88 | 89 | 91 | 98 |

99 | 104 |

105 |
110 | 111 | 113 | 119 |

120 | 125 |

126 |
130 | 131 |
132 | 133 | 134 | 135 | 136 | 137 | 154 | 155 | 156 | 157 | 158 | 175 | 176 | 177 |
labels['use-style']) ?> 138 | 145 | 146 | 153 |
labels['use-script']) ?> 159 | 166 | 167 | 174 |
178 | 179 |
180 | 181 | 182 | 183 | 184 | 194 | 195 | 196 | 197 | 198 | 201 | 209 | 210 | 211 | 214 | 222 | 223 | 224 | 227 | 235 | 236 | 237 | 238 | 239 | 240 |
241 |
242 | 247 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | 11 | defined('WP_UNINSTALL_PLUGIN') and delete_option('gm-cookie-policy'); 12 | --------------------------------------------------------------------------------