* @copyright Radek Polasek, www.broucek-a-beruska.cz * The buyer can free use/edit/modify this software in anyway * The buyer is NOT allowed to redistribute this module in anyway or resell it or redistribute it to third party */ class GDPR extends Module { function __construct() { $this->name = 'gdpr'; $this->tab = 'Tools'; $this->author = 'Brouček a Beruška Webdesign'; parent::__construct(); $this->page = basename(__FILE__, '.php'); $this->displayName = $this->l('GDPR - Privacy Policy'); $this->description = $this->l('Consent to processing personal data, request data, delete account'); } function install() { @Db::getInstance()->execute('ALTER TABLE '._DB_PREFIX_.'customer ADD COLUMN `gdpr_ip` varchar(255) NULL'); @Db::getInstance()->execute('ALTER TABLE '._DB_PREFIX_.'customer ADD COLUMN `gdpr_date_add` datetime NULL'); @Db::getInstance()->execute('ALTER TABLE '._DB_PREFIX_.'customer ADD COLUMN `gdpr_consent_sent` tinyint(1) unsigned NOT NULL DEFAULT "0"'); if (parent::install() AND Configuration::updateValue('GDPR_VALIDITY', 30) AND Configuration::updateValue('GDPR_REQUIRED', 1) AND Configuration::updateValue('GDPR_CART', 0) AND Configuration::updateValue('GDPR_REGISTER', 1) AND $this->registerHook('shoppingCart') AND $this->registerHook('createAccount') AND $this->registerHook('customerAccount') AND $this->registerHook('createAccountForm') AND $this->registerHook('header') AND $this->registerHook('newOrder') ) return true; return false; } public function uninstall() { if (parent::uninstall() AND $this->unregisterHook('shoppingCart') AND $this->unregisterHook('customerAccount') AND $this->unregisterHook('createAccount') AND $this->unregisterHook('createAccountForm') AND $this->unregisterHook('header') AND $this->unregisterHook('newOrder') AND Configuration::deleteByName('GDPR_ACCOUNT_DELETE') AND Configuration::deleteByName('GDPR_REQUEST_TYPE') AND Configuration::deleteByName('GDPR_VALIDITY') AND Configuration::deleteByName('GDPR_REQUIRED') AND Configuration::deleteByName('GDPR_REGISTER') AND Configuration::deleteByName('GDPR_CART') ) return true; return false; } function hookNewOrder($params) { $customer = $params['customer']; if (!Configuration::get('GDPR_REQUIRED') OR $customer->is_guest) return false; Db::getInstance()->Execute(' UPDATE '._DB_PREFIX_.'customer SET gdpr_ip = "'. $_SERVER['REMOTE_ADDR'] .'", gdpr_date_add = NOW() WHERE id_customer = '. $customer->id .' '); } private function getAgreement($id_customer) { $gdpr = Db::getInstance()->getRow(' SELECT DATEDIFF(gdpr_date_add, DATE_SUB(NOW(), INTERVAL '. Configuration::get('GDPR_VALIDITY') .' DAY)) > 0 AS agree FROM '._DB_PREFIX_.'customer WHERE id_customer = '. $id_customer .' '); return $gdpr['agree']; } function showRequest() { global $smarty, $cookie; if (Tools::isSubmit('request')) { $subject = $this->l('Request my data') .' - '. Configuration::get('PS_SHOP_NAME'); $customer = new Customer($cookie->id_customer); $templateVars = array( '{firstname}' => $customer->firstname, '{lastname}' => $customer->lastname, '{email}' => $customer->email ); // Manual retrieve if (Configuration::get('GDPR_REQUEST_TYPE')) { if (Mail::Send(intval($cookie->id_lang), 'request', $subject, $templateVars, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, dirname(__FILE__).'/mails/')) $report = 2; else $report = 1; } // Automatic e-mail with CSV file else { $types = array('customer', 'address', 'orders'); foreach ($types as $type) { $rows = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_ . $type .'` WHERE id_customer = '. $customer->id); foreach ($rows as $count=>$row) { $columns = $values = ''; foreach ($row as $key=>$value) { $columns .= $key .';'; $values .= $value .';'; } if (!$count) $data .= $columns ."\n"; $data .= $values ."\n"; } } $fileAttachment['content'] = $data; $fileAttachment['name'] = 'customer_'. $customer->id .'.csv'; $fileAttachment['mime'] = 'application/x-download'; if (Mail::Send(intval($cookie->id_lang), 'request_auto', $subject, $templateVars, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, dirname(__FILE__).'/mails/') AND Mail::Send(intval($cookie->id_lang), 'retrieve', $subject, $templateVars, $customer->email, $customer->firstname.' '.$customer->lastname, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), $fileAttachment, NULL, dirname(__FILE__).'/mails/') ) $report = 2; else $report = 1; } $smarty->assign('report', $report); } return $this->display(__FILE__, 'tpl/request.tpl'); } function showDelete() { global $smarty, $cookie; if (Tools::isSubmit('delete')) { $subject = $this->l('Delete and forget my account') .' - '. Configuration::get('PS_SHOP_NAME'); $customer = new Customer($cookie->id_customer); $templateVars = array( '{firstname}' => $customer->firstname, '{lastname}' => $customer->lastname, '{email}' => $customer->email ); if (Mail::Send(intval($cookie->id_lang), 'delete', $subject, $templateVars, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), $customer->email, $customer->firstname.' '.$customer->lastname, NULL, NULL, dirname(__FILE__).'/mails/')) $report = 2; else $report = 1; $smarty->assign('report', $report); } return $this->display(__FILE__, 'tpl/delete.tpl'); } function showConsent() { global $smarty, $cookie; $key = Tools::getValue('key'); $id_customer = Tools::getValue('id_customer') ? Tools::getValue('id_customer') : $cookie->id_customer; if ((Tools::isSubmit('consent') AND $cookie->id_customer) OR (Tools::isSubmit('consent') AND $key == _COOKIE_IV_)) { if (Tools::getValue('gdpr')) { Db::getInstance()->Execute(' UPDATE '._DB_PREFIX_.'customer SET gdpr_ip = "'. $_SERVER['REMOTE_ADDR'] .'", gdpr_date_add = NOW() WHERE id_customer = '. $id_customer .' '); } else { Db::getInstance()->Execute(' UPDATE '._DB_PREFIX_.'customer SET gdpr_ip = NULL, gdpr_date_add = NULL WHERE id_customer = '. $id_customer .' '); } $smarty->assign('report', $this->l('Settings updated')); } $xml = simplexml_load_file(dirname(__FILE__).'/gdpr.xml'); $smarty->assign(array( 'agree' => $this->getAgreement($id_customer), 'id_customer' => $id_customer, 'gdpr_consent' => $xml->{'consent_'.$cookie->id_lang} )); return $this->display(__FILE__, 'tpl/consent.tpl'); } function hookCustomerAccount($params) { global $smarty; $smarty->assign('account_delete', Configuration::get('GDPR_ACCOUNT_DELETE')); return $this->display(__FILE__, 'tpl/my-account.tpl'); } function hookCreateAccount($params) { if (!Configuration::get('GDPR_REGISTER')) return false; if (Tools::getValue('gdpr')) { Db::getInstance()->Execute(' UPDATE '._DB_PREFIX_.'customer SET gdpr_ip = "'. $_SERVER['REMOTE_ADDR'] .'", gdpr_date_add = NOW() WHERE id_customer = '. $params['newCustomer']->id .' '); } } function hookCreateAccountForm($params) { global $smarty, $cookie; if (!Configuration::get('GDPR_REGISTER')) return false; $xml = simplexml_load_file(dirname(__FILE__).'/gdpr.xml'); $smarty->assign(array( 'gdpr_consent' => $xml->{'consent_'.$cookie->id_lang}, 'gdpr_account_required' => Configuration::get('GDPR_REQUIRED')?1:0 )); return $this->display(__FILE__, 'tpl/create-account.tpl'); } function hookShoppingCart($params) { global $smarty, $cookie; if (!Configuration::get('GDPR_CART')) return false; $xml = simplexml_load_file(dirname(__FILE__).'/gdpr.xml'); $smarty->assign(array( 'agree' => $this->getAgreement($cookie->id_customer), 'id_customer' => $cookie->id_customer, 'gdpr_consent' => $xml->{'consent_'.$cookie->id_lang}, 'gdpr_required' => Configuration::get('GDPR_REQUIRED')?1:0 )); return $this->display(__FILE__, 'tpl/gdpr.tpl'); } function hookHeader($params) { global $smarty; return $this->display(__FILE__, 'tpl/header.tpl'); } public function getContent() { global $cookie, $currentIndex, $token; $defaultLanguage = intval(Configuration::get('PS_LANG_DEFAULT')); $languages = Language::getLanguages(); $iso = Language::getIsoById($defaultLanguage); $isoTinyMCE = (file_exists(_PS_ROOT_DIR_.'/js/tiny_mce/langs/'.$iso.'.js') ? $iso : 'en'); $ad = dirname($_SERVER["PHP_SELF"]); $divLangName = 'consent'; if (!Tools::getValue('orderby')) $orderby = 'id_customer'; else $orderby = Tools::getValue('orderby'); if (!Tools::getValue('orderway')) $orderway = 'DESC'; else $orderway = Tools::getValue('orderway'); $this->_html = '

'.$this->displayName.'

'; // Send consent if (Tools::isSubmit('sendConsent')) { $subject = $this->l('Request to consent with privacy policy') .' - '. Configuration::get('PS_SHOP_NAME'); $customer = new Customer(Tools::getValue('id_customer')); $templateVars = array( '{firstname}' => $customer->firstname, '{lastname}' => $customer->lastname, '{url}' => 'http'. (Configuration::get('PS_SSL_ENABLED')?'s':'') .'://' . htmlspecialchars($_SERVER['HTTP_HOST'], ENT_COMPAT, 'UTF-8') . __PS_BASE_URI__ .'modules/gdpr/consent.php?key='. _COOKIE_IV_ .'&id_customer='. $customer->id ); if (Mail::Send(intval($cookie->id_lang), 'consent', $subject, $templateVars, $customer->email, $customer->firstname.' '.$customer->lastname, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), NULL, NULL, dirname(__FILE__).'/mails/')) { Db::getInstance()->Execute('UPDATE '._DB_PREFIX_.'customer SET gdpr_consent_sent = 1 WHERE id_customer = '. $customer->id); $this->_html .= '
'. $this->l('Request sent').'
'; } else $this->_html .= '
'. $this->l('Error happened') .'
'; } // Delete if (Tools::isSubmit('submitDeleteCustomers')) { $customers = Db::getInstance()->ExecuteS('SELECT id_customer FROM '._DB_PREFIX_.'customer WHERE gdpr_ip IS NULL AND gdpr_date_add IS NULL'); foreach ($customers as $row) { $customer = new Customer($row['id_customer']); $customer->delete(); } $this->_html .= '
'. $this->l('Settings updated') .'
'; } // Settings if (Tools::isSubmit('submitSettings')) { $xml = ''."\n"; $xml .= ''."\n"; foreach ($languages as $language) $xml .= ''. htmlspecialchars(Tools::getValue('body_consent_'.$language['id_lang'])) .''."\n"; $xml .= ''."\n"; $file = fopen(dirname(__FILE__).'/gdpr.xml', 'w'); fwrite($file, $xml); fclose($file); if (Configuration::updateValue('GDPR_ACCOUNT_DELETE', Tools::getValue('account_delete')) AND Configuration::updateValue('GDPR_REQUEST_TYPE', Tools::getValue('request')) AND Configuration::updateValue('GDPR_VALIDITY', Tools::getValue('validity')) AND Configuration::updateValue('GDPR_REQUIRED', Tools::getValue('required')) AND Configuration::updateValue('GDPR_REGISTER', Tools::getValue('register')) AND Configuration::updateValue('GDPR_CART', Tools::getValue('cart')) ) $this->_html .= '
'. $this->l('Settings updated').'
'; else $this->_html .= '
'. $this->l('Error happened') .'
'; } $xml = simplexml_load_file(dirname(__FILE__).'/gdpr.xml'); $this->_html .= '
'.$this->l('Settings').'
'; foreach ($languages as $language) { $this->_html .= ''; } $this->_html .= $this->displayFlags($languages, $defaultLanguage, $divLangName, 'consent', true) . '

('.$this->l('Edit the text individually according to the specifics and settings of your e-shop.').')

('.$this->l('Check whether or not the consent agreement will be required by the customers.').')

('.$this->l('Check to show consent on shopping cart page.').')

('.$this->l('Check to show consent in customer registration form.').')

('.$this->l('Right to be forgotten').')

'.$this->l('Automatic email with CSV file from customer data').'
'.$this->l('Manually collect customer data by administrator and response').'

('.$this->l('Right to request').')

'. $this->l('days') .'

'; // Pagination initiation $type = Tools::getValue('type', 0); $num = Tools::getValue('num', 100); $page = Tools::getValue('page', 1); $nums = Array(100, 200, 500, 1000); $customers = Db::getInstance()->ExecuteS('SELECT id_customer FROM `'._DB_PREFIX_.'customer` WHERE is_guest = '. $type); $pages = ceil(count($customers)/$num); if ($page>$pages) $page = $pages; if ($page<1) $page = 1; $min = ($page-1)*$num; $max = $page*$num-1; $query_string = $currentIndex .'&configure=gdpr&token='. Tools::getValue('token') .'&orderby='. $orderby .'&orderway='. $orderway; $query_string2 = $currentIndex .'&configure=gdpr&token='. Tools::getValue('token') .'&type='.$type.'&page='.$page.'&num='. $num; $customers = Db::getInstance()->ExecuteS(' SELECT id_customer, lastname, firstname, email, active, gdpr_consent_sent, IF(gdpr_ip, gdpr_ip, "--") as gdpr_ip, IF(gdpr_date_add, gdpr_date_add, "--") as gdpr_date_add FROM '._DB_PREFIX_.'customer WHERE is_guest = '. $type .' ORDER BY '. $orderby .' '. $orderway .' LIMIT '. $min .','. $num .' '); $gdpr_customers = Db::getInstance()->ExecuteS('SELECT id_customer FROM '._DB_PREFIX_.'customer WHERE gdpr_ip IS NOT NULL AND gdpr_date_add IS NOT NULL'); $nogdpr_customers = Db::getInstance()->ExecuteS('SELECT id_customer FROM '._DB_PREFIX_.'customer WHERE gdpr_ip IS NULL AND gdpr_date_add IS NULL'); $this->_html .= '
Zákaznické souhlasy s GDPR

Dle GDPR pravidel je každý správce povinen doložit udělení souhlasu po celou dobu zpracování osobních údajů.

Zákazníků se souhlasem: '. count($gdpr_customers) .'

Zákazníků bez souhlasu: '. count($nogdpr_customers) .'

'; // Filtering $this->_html .= '

'. $this->l('Page') .': '; // Pages $start = $page - 10; if ($start < 1) $start = 1; $stop = $page + 10; if ($stop > $pages) $stop = $pages; if ($page!=1) $this->_html .= '<< '; if ($start>10) $this->_html .= ' 1 ... '; for ($i=$start; $i<=$stop; $i++) { if ($i>$start) $this->_html .= ' | '; if ($page==$i) { $this->_html .= ''. $i .''; } else { $this->_html .= ''. $i .''; } } if ($pages>$stop+10) $this->_html .= ' ... '. $pages .''; if ($page!=$pages) $this->_html .= ' >>'; // Number of customers $this->_html .= ''. $this->l('View'). ': '; if ($type) $this->_html .= ' Registrovaní | Neregistrovaní'; else $this->_html .= ' Registrovaní | Neregistrovaní'; $this->_html .= '

'; $i = 0; foreach ($customers as $customer) { $i++; $this->_html .= ' '; } $this->_html .= '
ID Příjmení Jméno E-mail Aktivní IP adresa Datum souhlasu Žádost o souhlas
'. $customer['id_customer'] .' '. $customer['lastname'] .' '. $customer['firstname'] .' '. $customer['email'] .' '. $customer['gdpr_ip'] .' '. $customer['gdpr_date_add'] .' '; if ($customer['gdpr_consent_sent']) $this->_html .= 'odeslána'; elseif ($customer['gdpr_ip'] == '--') $this->_html .= '
'; $this->_html .= '

Libovolný zákazník může vyslovit souhlas se zpracováním osobních údajů také na stránce:
'. $_SERVER['HTTP_HOST'] . __PS_BASE_URI__ .'modules/gdpr/consent.php?key='. _COOKIE_IV_ .'&id_customer=@, kde v url nahraďte znak @ za ID daného zákazníka.

Údaje o zákaznících, od kterých není nově získaný souhlas se zpracováním jejich osobních údajů, je správce povinen vymazat, pominul-li zákonný důvod jejich případné archivace.

('. $this->l('deletes customer and addresses database records') .')

Nápověda

Obecně máte povinnost získavat a uchovávat pouze údaje nezbytně nutné k provedení objednávky. Typicky nadbytečné je např. pohlaví (patrné ze jména), datum narození nebo věk.

  1. Správce - provozovatel e-shopu
  2. Zpracovatel - je pověřován správcem, může to být zaměstnanec e-shopu, účetní, dopravce nebo hosting (mějte proto se zpracovatelem uzavřenou zpracovatelskou smlouvu)
  3. Pověřenec - je pověřován správcem, provádí dohled nad plněním GDPR, nenese ale odpovědnost a netýká se zpracování osobních údajů v menší míře (malý e-shop) a pro osobní či domácí účely. Pro přesné určení nutnosti mít pověřence může posloužit GDPR audit Vašeho konkrétního e-shopu.

Provozovatel e-shopu by měl mít v rámci GDPR jakožto správce sepsané záznamy o činnostech v rámci GDPR, které musí obsahovat následující informace (musí být poskytnuty na vyžádání dozorového úřadu):

Viz. ukázka.

S přihlédnutím ke stavu techniky, nákladům na provedení, povaze, rozsahu, kontextu a účelům zpracování i k různě pravděpodobným a různě závažným rizikům pro práva a svobody fyzických osob, provedou správce a zpracovatel vhodná technická a organizační opatření, aby zajistili úroveň zabezpečení odpovídající danému riziku a případně dále zajistí:

Více na: www.gdpr.cz.

Ohledně individuálních úprav GDPR na Vašem e-shopu nás kontaktujte na e-mailu info@broucek-a-beruska.cz.

'; return $this->_html; } } ?>