From 69b4ecc0f8957935434751b2d58454ef260b86eb Mon Sep 17 00:00:00 2001
From: emanuele
Date: Sat, 12 May 2018 20:57:13 +0200
Subject: [PATCH 01/13] Initial work on "accept agreement": form for accepting,
backup and log
---
install/install_1-1.php | 17 +++++
install/upgrade_1-1.php | 30 +++++++++
sources/Load.php | 8 +++
sources/Subs.php | 65 +++++++++++++++----
.../admin/ManageRegistration.controller.php | 16 ++++-
sources/controllers/Register.controller.php | 49 ++++++++++++++
sources/subs/Agreement.class.php | 55 +++++++++++++++-
sources/subs/Members.subs.php | 24 +++++++
themes/default/Register.template.php | 6 +-
themes/default/Theme.php | 5 ++
themes/default/index.template.php | 28 ++++++++
.../languages/english/Admin.english.php | 2 +
.../languages/english/Login.english.php | 1 +
.../languages/english/Profile.english.php | 3 +
.../languages/english/index.english.php | 1 +
15 files changed, 294 insertions(+), 16 deletions(-)
diff --git a/install/install_1-1.php b/install/install_1-1.php
index 1cbc2f3825..d5b949df56 100644
--- a/install/install_1-1.php
+++ b/install/install_1-1.php
@@ -1153,6 +1153,23 @@ public function table_log_activity()
);
}
+ public function table_log_agreement_accept()
+ {
+ return $this->table->db_create_table('{db_prefix}log_agreement_accept',
+ array(
+ array('name' => 'agreement_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
+ array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
+ ),
+ array(
+ array('name' => 'modify_date', 'columns' => array('modify_date', 'id_member'), 'type' => 'primary'),
+ ),
+ array(),
+ 'ignore'
+ );
+ }
+
public function table_log_badbehavior()
{
return $this->table->db_create_table('{db_prefix}log_badbehavior',
diff --git a/install/upgrade_1-1.php b/install/upgrade_1-1.php
index 82c4be98de..36014eeb9a 100644
--- a/install/upgrade_1-1.php
+++ b/install/upgrade_1-1.php
@@ -865,4 +865,34 @@ public function update_settings()
)
);
}
+
+ public function agreement_logging_title()
+ {
+ return 'Introducing the logging of accepted agreement...';
+ }
+
+ public function agreement_logging()
+ {
+ return array(
+ array(
+ 'debug_title' => 'Creating the table...',
+ 'function' => function()
+ {
+ $this->table->db_create_table('{db_prefix}log_agreement_accept',
+ array(
+ array('name' => 'agreement_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
+ array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
+ ),
+ array(
+ array('name' => 'modify_date', 'columns' => array('modify_date', 'id_member'), 'type' => 'primary'),
+ ),
+ array(),
+ 'ignore'
+ );
+ }
+ )
+ );
+ }
}
diff --git a/sources/Load.php b/sources/Load.php
index eb3c66b37c..b9bc72bd7c 100644
--- a/sources/Load.php
+++ b/sources/Load.php
@@ -430,6 +430,14 @@ function loadUserSettings()
else
$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
+ if ($user_info['is_guest'] === false)
+ {
+ $http_request = HttpReq::instance();
+ if (!empty($modSettings['agreementRevision']) && !empty($modSettings['requireAgreement']) && in_array($http_request->query->action, array('reminder', 'register')) === false)
+ {
+ checkAcceptedAgreement($id_member, $modSettings['agreementRevision']);
+ }
+ }
call_integration_hook('integrate_user_info');
}
diff --git a/sources/Subs.php b/sources/Subs.php
index 3bb30db4ef..92194674d0 100644
--- a/sources/Subs.php
+++ b/sources/Subs.php
@@ -926,6 +926,21 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
// Need user agent
$req = request();
+ setOldUrl();
+
+ // For session check verification.... don't switch browsers...
+ $_SESSION['USER_AGENT'] = $req->user_agent();
+
+ // Hand off the output to the portal, etc. we're integrated with.
+ call_integration_hook('integrate_exit', array($do_footer));
+
+ // Don't exit if we're coming from index.php; that will pass through normally.
+ if (!$from_index)
+ exit;
+}
+
+function setOldUrl($index = 'old_url')
+{
// Remember this URL in case someone doesn't like sending HTTP_REFERER.
$invalid_old_url = array(
'action=dlattach',
@@ -945,17 +960,9 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
}
}
if ($make_old === true)
- $_SESSION['old_url'] = $_SERVER['REQUEST_URL'];
-
- // For session check verification.... don't switch browsers...
- $_SESSION['USER_AGENT'] = $req->user_agent();
-
- // Hand off the output to the portal, etc. we're integrated with.
- call_integration_hook('integrate_exit', array($do_footer));
-
- // Don't exit if we're coming from index.php; that will pass through normally.
- if (!$from_index)
- exit;
+ {
+ $_SESSION[$index] = $_SERVER['REQUEST_URL'];
+ }
}
/**
@@ -2109,4 +2116,40 @@ function obStart($use_compression = false)
ob_start();
header('Content-Encoding: none');
}
+}
+
+function checkAcceptedAgreement($id_member, $agreement_date)
+{
+ global $context, $txt;
+
+ $db = database();
+
+ $accepted = $db->fetchQuery('
+ SELECT 1
+ FROM {db_prefix}log_agreement_accept
+ WHERE agreement_date = {date:agreement_date}
+ AND id_member = {int:id_member}',
+ array(
+ 'id_member' => $id_member,
+ 'agreement_date' => $agreement_date,
+ )
+ );
+
+ if (empty($accepted))
+ {
+ setOldUrl('agreement_url_redirect');
+ redirectexit('action=register;sa=agreement', true);
+ }
+ if (!empty($_SESSION['agreement_accepted']))
+ {
+ // This loadLanguage is needed here because the check is done way too early in the process.
+ // As a stop-gap it's fine, but in future versions it should be fixed somehow.
+ loadLanguage('index');
+ $_SESSION['agreement_accepted'] = null;
+ $context['accepted_agreement'] = array(
+ 'errors' => array(
+ 'accepted_agreement' => $txt['agreement_accepted']
+ )
+ );
+ }
}
\ No newline at end of file
diff --git a/sources/admin/ManageRegistration.controller.php b/sources/admin/ManageRegistration.controller.php
index 62e1dbb257..81c8603a5e 100644
--- a/sources/admin/ManageRegistration.controller.php
+++ b/sources/admin/ManageRegistration.controller.php
@@ -271,6 +271,7 @@ public function action_agreement()
}
}
+ $context['warning'] = '';
$agreement = new \Agreement($context['current_agreement']);
if (isset($this->_req->post->save) && isset($this->_req->post->agreement))
@@ -279,14 +280,25 @@ public function action_agreement()
validateToken('admin-rega');
// Off it goes to the agreement file.
- $agreement->save($this->_req->post->agreement);
+ $success = $agreement->save($this->_req->post->agreement, !empty($this->_req->post->checkboxAcceptAgreement));
+ if (!empty($this->_req->post->checkboxAcceptAgreement))
+ {
+ if ($success === false)
+ {
+ $context['warning'] .= $txt['agreement_backup_not_writable'] . ' ';
+ }
+ else
+ {
+ updateSettings(array('agreementRevision' => $success));
+ }
+ }
updateSettings(array('requireAgreement' => !empty($this->_req->post->requireAgreement), 'checkboxAgreement' => !empty($this->_req->post->checkboxAgreement)));
}
$context['agreement'] = Util::htmlspecialchars($agreement->getPlainText(false));
- $context['warning'] = $agreement->isWritable() ? '' : $txt['agreement_not_writable'];
+ $context['warning'] .= $agreement->isWritable() ? '' : $txt['agreement_not_writable'];
$context['require_agreement'] = !empty($modSettings['requireAgreement']);
$context['checkbox_agreement'] = !empty($modSettings['checkboxAgreement']);
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index e8d9306cf7..a035acdfb8 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -108,6 +108,13 @@ public function action_register()
{
global $txt, $context, $modSettings, $user_info, $scripturl;
+ // If we are here only to accept the agreement, then let's do that.
+ if (isset($this->_req->post->accept_agreement))
+ {
+ $this->action_agreement();
+ return;
+ }
+
// If this user is an admin - redirect them to the admin registration page.
if (allowedTo('moderate_forum') && !$user_info['is_guest'])
redirectexit('action=admin;area=regcenter;sa=register');
@@ -1240,4 +1247,46 @@ public function action_registerCheckUsername()
$context['valid_username'] = !$errors->hasErrors();
}
+
+ public function action_agreement()
+ {
+ global $context, $user_info, $modSettings;
+
+ if (isset($this->_req->post->accept_agreement))
+ {
+ require_once(SUBSDIR . '/Members.subs.php');
+ registerAgreementAccepted($user_info['id'], $user_info['ip'], empty($modSettings['agreementRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['agreementRevision']);
+ $_SESSION['agreement_accepted'] = true;
+ if (isset($_SESSION['agreement_url_redirect']))
+ {
+ redirectexit($_SESSION['agreement_url_redirect']);
+ }
+ }
+ elseif (isset($this->_req->post->no_accept))
+ {
+ // This should lead to the account deletion page
+ }
+ else
+ {
+ $context['sub_template'] = 'registration_agreement';
+ }
+
+ loadLanguage('Login');
+ loadLanguage('Profile');
+ loadTemplate('Register');
+ // If you have to agree to the agreement, it needs to be fetched from the file.
+ $agreement = new \Agreement($user_info['language']);
+ $context['agreement'] = $agreement->getParsedText();
+
+ $context['show_coppa'] = !empty($modSettings['coppaAge']);
+ $context['show_contact_button'] = !empty($modSettings['enable_contactform']) && $modSettings['enable_contactform'] === 'registration';
+ // Under age restrictions?
+ if ($context['show_coppa'])
+ {
+ $context['skip_coppa'] = false;
+ $context['coppa_agree_above'] = sprintf($txt[($context['require_agreement'] ? 'agreement_' : '') . 'agree_coppa_above'], $modSettings['coppaAge']);
+ $context['coppa_agree_below'] = sprintf($txt[($context['require_agreement'] ? 'agreement_' : '') . 'agree_coppa_below'], $modSettings['coppaAge']);
+ }
+ createToken('register');
+ }
}
diff --git a/sources/subs/Agreement.class.php b/sources/subs/Agreement.class.php
index cc86162ab9..6630def4ca 100644
--- a/sources/subs/Agreement.class.php
+++ b/sources/subs/Agreement.class.php
@@ -25,14 +25,27 @@ class Agreement
*/
protected $_language = '';
+ /**
+ * The directory where backups are stored
+ *
+ * @var string
+ */
+ protected $_backup_dir = '';
+
/**
* Everything starts here.
*
* @param string $language the wanted language of the agreement.
+ * @param string $backup_dir where to store the backup of the agreements.
*/
- public function __construct($language)
+ public function __construct($language, $backup_dir = null)
{
$this->_language = strtr($language, array('.' => ''));
+ if ($backup_dir === null || file_exists($backup_dir) === false)
+ {
+ $backup_dir = BOARDDIR . '/packages/backups/agreements';
+ }
+ $this->_backup_dir = $backup_dir;
}
/**
@@ -41,13 +54,26 @@ public function __construct($language)
* If the language passed to the class is empty, then it uses agreement.txt.
*
* @param string $text the language of the agreement we want.
+ * @param bool $update_backup if store a copy of the text of the agreements.
*/
- public function save($text)
+ public function save($text, $update_backup = false)
{
+ $backup_id = '';
+ if ($update_backup === true)
+ {
+ $backup_id = strftime('%Y-%m-%d', forum_time(false));
+ if ($this->_createBackup($backup_id) === false)
+ {
+ $backup_id = false;
+ }
+ }
+
// Off it goes to the agreement file.
$fp = fopen(BOARDDIR . '/agreement' . $this->normalizeLanguage() . '.txt', 'w');
fwrite($fp, str_replace("\r", '', $text));
fclose($fp);
+
+ return $backup_id;
}
/**
@@ -114,4 +140,29 @@ protected function normalizeLanguage()
{
return $this->_language === '' ? '' : '.' . $this->_language;
}
+
+ /**
+ * Creates a full backup of all the agreements.
+ *
+ * @param string $backup_id the name of the directory of the backup
+ * @return bool true if successful, false if failes to create the directory
+ */
+ protected function _createBackup($backup_id)
+ {
+ $destination = $this->_backup_dir . '/' . $backup_id . '/';
+ if (file_exists($this->_backup_dir) === false)
+ {
+ mkdir($this->_backup_dir);
+ }
+ if (mkdir($destination) === false)
+ {
+ return false;
+ }
+ $glob = new GlobIterator(BOARDDIR . '/agreement*.txt', FilesystemIterator::SKIP_DOTS);
+ foreach ($glob as $file)
+ {
+ copy($file->getPathname(), $destination . $file->getBasename());
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/sources/subs/Members.subs.php b/sources/subs/Members.subs.php
index 8f0b73413d..b271b90e13 100644
--- a/sources/subs/Members.subs.php
+++ b/sources/subs/Members.subs.php
@@ -2607,3 +2607,27 @@ function loadMembersIPs($ip_string, $ip_var)
return $ips;
}
+
+function registerAgreementAccepted($id_member, $ip, $agreement_version)
+{
+ $db = database();
+
+ $db->insert('',
+ '{db_prefix}log_agreement_accept',
+ array(
+ 'agreement_date' => 'date',
+ 'id_member' => 'int',
+ 'accepted_date' => 'date',
+ 'accepted_ip' => 'string-255',
+ ),
+ array(
+ array(
+ 'agreement_date' => $agreement_version,
+ 'id_member' => $id_member,
+ 'accepted_date' => strftime('%Y-%m-%d', forum_time(false)),
+ 'accepted_ip' => $ip,
+ )
+ ),
+ array('agreement_date', 'id_member')
+ );
+}
\ No newline at end of file
diff --git a/themes/default/Register.template.php b/themes/default/Register.template.php
index 9403d4c7c1..346fa162be 100644
--- a/themes/default/Register.template.php
+++ b/themes/default/Register.template.php
@@ -60,6 +60,8 @@ function template_registration_agreement()
else
echo '
';
+ echo '
+ ';
if ($context['show_contact_button'])
echo '
@@ -764,12 +766,14 @@ function template_edit_agreement()
// Show the actual agreement in an oversized text box.
echo '
-
+
+
+
diff --git a/themes/default/Theme.php b/themes/default/Theme.php
index f85fa16923..71be249cfd 100644
--- a/themes/default/Theme.php
+++ b/themes/default/Theme.php
@@ -461,6 +461,11 @@ public function template_admin_warning_above()
template_show_error('new_version_updates');
}
+ if (!empty($context['accepted_agreement']))
+ {
+ template_show_error('accepted_agreement');
+ }
+
// Any special notices to remind the admin about?
if (!empty($context['warning_controls']))
{
diff --git a/themes/default/index.template.php b/themes/default/index.template.php
index 6a12be8991..d736c5bb42 100644
--- a/themes/default/index.template.php
+++ b/themes/default/index.template.php
@@ -401,6 +401,19 @@ function template_uc_news_fader()
}
}
+/**
+ * All your data are belong to us (cit.)
+ */
+function template_uc_agreement_accepted()
+{
+ global $txt;
+
+ echo '
+
+
', $txt['agreement_accepted'], '
+
';
+}
+
/**
* Section down the page, before closing body
*/
@@ -795,6 +808,21 @@ function template_show_error($error_id)
';
}
+function template_uc_generic_infobox()
+{
+ global $context;
+
+ if (empty($context['generic_infobox']))
+ {
+ return;
+ }
+
+ foreach ($context['generic_infobox'] as $key)
+ {
+ template_show_error($key);
+ }
+}
+
/**
* Another used and abused piece of template that can be found everywhere
*
diff --git a/themes/default/languages/english/Admin.english.php b/themes/default/languages/english/Admin.english.php
index ca9834bdc8..4ca2ccd2ed 100644
--- a/themes/default/languages/english/Admin.english.php
+++ b/themes/default/languages/english/Admin.english.php
@@ -47,6 +47,7 @@
$txt['admin_credits'] = 'Credits';
$txt['admin_agreement'] = 'Show and require agreement letter when registering';
$txt['admin_checkbox_agreement'] = 'Show a checkbox for the agreement in registration form instead of a full page';
+$txt['admin_checkbox_accept_agreement'] = 'Force members to accept the agreement at the next login';
$txt['admin_agreement_default'] = 'Default';
$txt['admin_agreement_select_language'] = 'Language to edit';
$txt['admin_agreement_select_language_change'] = 'Change';
@@ -124,6 +125,7 @@
$txt['live'] = 'Latest Software Updates';
$txt['remove_all'] = 'Clear Log';
$txt['agreement_not_writable'] = 'Warning - agreement.txt is not writable, any changes you make will NOT be saved.';
+$txt['agreement_backup_not_writable'] = 'Warning - the backup directory in forum_root/packages/backup cannot be created.';
$txt['version_check_desc'] = 'This shows you the versions of your installation\'s files versus those of the latest version. If any of these files are out of date, you should download and upgrade to the latest version at our ElkArte Site.';
$txt['version_check_more'] = '(more detailed)';
diff --git a/themes/default/languages/english/Login.english.php b/themes/default/languages/english/Login.english.php
index 850d2a7454..a0c7b2d687 100644
--- a/themes/default/languages/english/Login.english.php
+++ b/themes/default/languages/english/Login.english.php
@@ -4,6 +4,7 @@
// Registration agreement page.
$txt['registration_agreement'] = 'Registration Agreement';
$txt['agreement_agree'] = 'I accept the terms of the agreement.';
+$txt['agreement_no_agree'] = 'I do not accept the terms of the agreement.';
$txt['agreement_agree_coppa_above'] = 'I accept the terms of the agreement and I am at least %1$d years old.';
$txt['agreement_agree_coppa_below'] = 'I accept the terms of the agreement and I am younger than %1$d years old.';
$txt['agree_coppa_above'] = 'I am at least %1$d years old.';
diff --git a/themes/default/languages/english/Profile.english.php b/themes/default/languages/english/Profile.english.php
index 87068f9c1e..ba9e48d34e 100644
--- a/themes/default/languages/english/Profile.english.php
+++ b/themes/default/languages/english/Profile.english.php
@@ -86,6 +86,9 @@
$txt['reminder_openid_is'] = 'The OpenID identity associated with your account is: %1$s
Please make a note of this for future reference.';
$txt['reminder_continue'] = 'Continue';
+$txt['accept_agreement_title'] = 'Accept agreement';
+$txt['agreement_accepted_title'] = 'Continue';
+
$txt['current_theme'] = 'Current Theme';
$txt['change'] = 'Change Theme';
$txt['theme_forum_default'] = 'Forum or Board Default';
diff --git a/themes/default/languages/english/index.english.php b/themes/default/languages/english/index.english.php
index 7ce43e7d18..799f178832 100644
--- a/themes/default/languages/english/index.english.php
+++ b/themes/default/languages/english/index.english.php
@@ -550,6 +550,7 @@
$txt['not_removed_extra'] = '%1$s is a backup of %2$s that was not generated by ElkArte. It can be accessed directly and used to gain unauthorised access to your forum. You should delete it immediately.';
$txt['generic_warning'] = 'Warning';
$txt['agreement_missing'] = 'You are requiring new users to accept a registration agreement, however the file (agreement.txt) doesn\'t exist.';
+$txt['agreement_accepted'] = 'You have successfully accepted the agreement.';
$txt['new_version_updates'] = 'You have just updated!';
$txt['new_version_updates_text'] = 'Click here to see what\'s new in this version of ElkArte!!';
From b754d5ad82102727e4eb66d120f407456ff36b35 Mon Sep 17 00:00:00 2001
From: emanuele
Date: Sat, 12 May 2018 23:01:12 +0200
Subject: [PATCH 02/13] In case of not wanting to accept the agreement...
delete is the only solution
---
sources/Load.php | 5 ++++-
sources/controllers/Register.controller.php | 4 ++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/sources/Load.php b/sources/Load.php
index b9bc72bd7c..117ca33885 100644
--- a/sources/Load.php
+++ b/sources/Load.php
@@ -435,7 +435,10 @@ function loadUserSettings()
$http_request = HttpReq::instance();
if (!empty($modSettings['agreementRevision']) && !empty($modSettings['requireAgreement']) && in_array($http_request->query->action, array('reminder', 'register')) === false)
{
- checkAcceptedAgreement($id_member, $modSettings['agreementRevision']);
+ if ($http_request->query->action !== 'profile' || $http_request->area !== 'deleteaccount')
+ {
+ checkAcceptedAgreement($id_member, $modSettings['agreementRevision']);
+ }
}
}
call_integration_hook('integrate_user_info');
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index a035acdfb8..1b5443b434 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -109,7 +109,7 @@ public function action_register()
global $txt, $context, $modSettings, $user_info, $scripturl;
// If we are here only to accept the agreement, then let's do that.
- if (isset($this->_req->post->accept_agreement))
+ if (isset($this->_req->post->accept_agreement) || isset($this->_req->post->no_accept))
{
$this->action_agreement();
return;
@@ -1264,7 +1264,7 @@ public function action_agreement()
}
elseif (isset($this->_req->post->no_accept))
{
- // This should lead to the account deletion page
+ redirectexit('action=profile;area=deleteaccount');
}
else
{
From 0e4160dcc4b2027193ce4b543723bbb546b1faa5 Mon Sep 17 00:00:00 2001
From: emanuele
Date: Sun, 13 May 2018 12:25:34 +0200
Subject: [PATCH 03/13] Move agreement accepted check to Agreement class
---
sources/Load.php | 21 ++++++++++++++++++-
sources/Subs.php | 36 --------------------------------
sources/subs/Agreement.class.php | 30 ++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 37 deletions(-)
diff --git a/sources/Load.php b/sources/Load.php
index 117ca33885..6e6e205c7d 100644
--- a/sources/Load.php
+++ b/sources/Load.php
@@ -437,7 +437,26 @@ function loadUserSettings()
{
if ($http_request->query->action !== 'profile' || $http_request->area !== 'deleteaccount')
{
- checkAcceptedAgreement($id_member, $modSettings['agreementRevision']);
+ $agreement = new Agreement($user_info['language']);
+ if (false === $agreement->checkAccepted($id_member, $modSettings['agreementRevision']))
+ {
+
+ setOldUrl('agreement_url_redirect');
+ redirectexit('action=register;sa=agreement', true);
+ }
+
+ if (!empty($_SESSION['agreement_accepted']))
+ {
+ // This loadLanguage is needed here because the check is done way too early in the process.
+ // As a stop-gap it's fine, but in future versions it should be fixed somehow.
+ loadLanguage('index');
+ $_SESSION['agreement_accepted'] = null;
+ $context['accepted_agreement'] = array(
+ 'errors' => array(
+ 'accepted_agreement' => $txt['agreement_accepted']
+ )
+ );
+ }
}
}
}
diff --git a/sources/Subs.php b/sources/Subs.php
index 92194674d0..d7d5d4f71b 100644
--- a/sources/Subs.php
+++ b/sources/Subs.php
@@ -2116,40 +2116,4 @@ function obStart($use_compression = false)
ob_start();
header('Content-Encoding: none');
}
-}
-
-function checkAcceptedAgreement($id_member, $agreement_date)
-{
- global $context, $txt;
-
- $db = database();
-
- $accepted = $db->fetchQuery('
- SELECT 1
- FROM {db_prefix}log_agreement_accept
- WHERE agreement_date = {date:agreement_date}
- AND id_member = {int:id_member}',
- array(
- 'id_member' => $id_member,
- 'agreement_date' => $agreement_date,
- )
- );
-
- if (empty($accepted))
- {
- setOldUrl('agreement_url_redirect');
- redirectexit('action=register;sa=agreement', true);
- }
- if (!empty($_SESSION['agreement_accepted']))
- {
- // This loadLanguage is needed here because the check is done way too early in the process.
- // As a stop-gap it's fine, but in future versions it should be fixed somehow.
- loadLanguage('index');
- $_SESSION['agreement_accepted'] = null;
- $context['accepted_agreement'] = array(
- 'errors' => array(
- 'accepted_agreement' => $txt['agreement_accepted']
- )
- );
- }
}
\ No newline at end of file
diff --git a/sources/subs/Agreement.class.php b/sources/subs/Agreement.class.php
index 6630def4ca..7c6ef58f8b 100644
--- a/sources/subs/Agreement.class.php
+++ b/sources/subs/Agreement.class.php
@@ -32,6 +32,13 @@ class Agreement
*/
protected $_backup_dir = '';
+ /**
+ * The database object
+ *
+ * @var Object
+ */
+ protected $_db = null;
+
/**
* Everything starts here.
*
@@ -46,6 +53,7 @@ public function __construct($language, $backup_dir = null)
$backup_dir = BOARDDIR . '/packages/backups/agreements';
}
$this->_backup_dir = $backup_dir;
+ $this->_db = database();
}
/**
@@ -131,6 +139,28 @@ public function isWritable()
return file_exists($filename) && is_writable($filename);
}
+ /**
+ * Test if the user accepted the current agreement or not.
+ *
+ * @param int $id_member The id of the member
+ * @param string $agreement_date The date of the agreement
+ */
+ public function checkAccepted($id_member, $agreement_date)
+ {
+ $accepted = $this->_db->fetchQuery('
+ SELECT 1
+ FROM {db_prefix}log_agreement_accept
+ WHERE agreement_date = {date:agreement_date}
+ AND id_member = {int:id_member}',
+ array(
+ 'id_member' => $id_member,
+ 'agreement_date' => $agreement_date,
+ )
+ );
+
+ return empty($accepted);
+ }
+
/**
* Takes care of the edge-case of the default agreement that doesn't have
* the language in the name, and the fact that the admin panels loads it
From 516352f40aaec090d9724660dc514f4c7808e71c Mon Sep 17 00:00:00 2001
From: emanuele
Date: Sun, 13 May 2018 12:32:20 +0200
Subject: [PATCH 04/13] Revise the way the template_registration_agreement
works adding an optional sub action hidden input field
---
sources/controllers/Register.controller.php | 9 ++-------
sources/subs/Agreement.class.php | 2 +-
themes/default/Register.template.php | 6 ++++++
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index 1b5443b434..4d8b8ceebd 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -84,6 +84,7 @@ public function action_index()
'verificationcode' => array($this, 'action_verificationcode'),
'coppa' => array($this, 'action_coppa'),
'agrelang' => array($this, 'action_agrelang'),
+ 'agreement' => array($this, 'action_agreement'),
);
// Setup the action handler
@@ -108,13 +109,6 @@ public function action_register()
{
global $txt, $context, $modSettings, $user_info, $scripturl;
- // If we are here only to accept the agreement, then let's do that.
- if (isset($this->_req->post->accept_agreement) || isset($this->_req->post->no_accept))
- {
- $this->action_agreement();
- return;
- }
-
// If this user is an admin - redirect them to the admin registration page.
if (allowedTo('moderate_forum') && !$user_info['is_guest'])
redirectexit('action=admin;area=regcenter;sa=register');
@@ -1269,6 +1263,7 @@ public function action_agreement()
else
{
$context['sub_template'] = 'registration_agreement';
+ $context['register_subaction'] = 'agreement';
}
loadLanguage('Login');
diff --git a/sources/subs/Agreement.class.php b/sources/subs/Agreement.class.php
index 7c6ef58f8b..b3b82d53ed 100644
--- a/sources/subs/Agreement.class.php
+++ b/sources/subs/Agreement.class.php
@@ -158,7 +158,7 @@ public function checkAccepted($id_member, $agreement_date)
)
);
- return empty($accepted);
+ return !empty($accepted);
}
/**
diff --git a/themes/default/Register.template.php b/themes/default/Register.template.php
index 346fa162be..12a0fff6f5 100644
--- a/themes/default/Register.template.php
+++ b/themes/default/Register.template.php
@@ -68,6 +68,12 @@ function template_registration_agreement()
';
+ if (!empty($context['register_subaction']))
+ {
+ echo '
+ ';
+ }
+
echo '
From 321d87e65952a495624a924261e47ce8f307ae54 Mon Sep 17 00:00:00 2001
From: emanuele
Date: Sun, 20 May 2018 21:45:25 +0200
Subject: [PATCH 05/13] Agreement class a bit more flexibly (for later
accepting privacy policy as well)
---
install/install_1-1.php | 21 +++++-
install/upgrade_1-1.php | 4 +-
sources/Load.php | 29 ++++-----
sources/controllers/Register.controller.php | 5 +-
sources/subs/Agreement.class.php | 71 +++++++++++++++++----
sources/subs/Members.subs.php | 8 +--
6 files changed, 100 insertions(+), 38 deletions(-)
diff --git a/install/install_1-1.php b/install/install_1-1.php
index d5b949df56..4ede073943 100644
--- a/install/install_1-1.php
+++ b/install/install_1-1.php
@@ -1157,13 +1157,13 @@ public function table_log_agreement_accept()
{
return $this->table->db_create_table('{db_prefix}log_agreement_accept',
array(
- array('name' => 'agreement_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
),
array(
- array('name' => 'modify_date', 'columns' => array('modify_date', 'id_member'), 'type' => 'primary'),
+ array('name' => 'version', 'columns' => array('version', 'id_member'), 'type' => 'primary'),
),
array(),
'ignore'
@@ -2421,6 +2421,23 @@ public function table_postby_emails_filters()
);
}
+ public function table_log_privacy_policy_accept()
+ {
+ return $this->table->db_create_table('{db_prefix}log_privacy_policy_accept',
+ array(
+ array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
+ array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
+ ),
+ array(
+ array('name' => 'version', 'columns' => array('version', 'id_member'), 'type' => 'primary'),
+ ),
+ array(),
+ 'ignore'
+ );
+ }
+
public function table_scheduled_tasks()
{
return $this->table->db_create_table('{db_prefix}scheduled_tasks',
diff --git a/install/upgrade_1-1.php b/install/upgrade_1-1.php
index 36014eeb9a..1d8da34fa2 100644
--- a/install/upgrade_1-1.php
+++ b/install/upgrade_1-1.php
@@ -880,13 +880,13 @@ public function agreement_logging()
{
$this->table->db_create_table('{db_prefix}log_agreement_accept',
array(
- array('name' => 'agreement_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
),
array(
- array('name' => 'modify_date', 'columns' => array('modify_date', 'id_member'), 'type' => 'primary'),
+ array('name' => 'version', 'columns' => array('version', 'id_member'), 'type' => 'primary'),
),
array(),
'ignore'
diff --git a/sources/Load.php b/sources/Load.php
index 6e6e205c7d..028343c081 100644
--- a/sources/Load.php
+++ b/sources/Load.php
@@ -433,9 +433,9 @@ function loadUserSettings()
if ($user_info['is_guest'] === false)
{
$http_request = HttpReq::instance();
- if (!empty($modSettings['agreementRevision']) && !empty($modSettings['requireAgreement']) && in_array($http_request->query->action, array('reminder', 'register')) === false)
+ if (!empty($modSettings['agreementRevision']) && !empty($modSettings['requireAgreement']) && in_array($http_request->action, array('reminder', 'register')) === false)
{
- if ($http_request->query->action !== 'profile' || $http_request->area !== 'deleteaccount')
+ if ($http_request->action !== 'profile' || $http_request->area !== 'deleteaccount')
{
$agreement = new Agreement($user_info['language']);
if (false === $agreement->checkAccepted($id_member, $modSettings['agreementRevision']))
@@ -444,19 +444,6 @@ function loadUserSettings()
setOldUrl('agreement_url_redirect');
redirectexit('action=register;sa=agreement', true);
}
-
- if (!empty($_SESSION['agreement_accepted']))
- {
- // This loadLanguage is needed here because the check is done way too early in the process.
- // As a stop-gap it's fine, but in future versions it should be fixed somehow.
- loadLanguage('index');
- $_SESSION['agreement_accepted'] = null;
- $context['accepted_agreement'] = array(
- 'errors' => array(
- 'accepted_agreement' => $txt['agreement_accepted']
- )
- );
- }
}
}
}
@@ -1650,6 +1637,18 @@ function loadTheme($id_theme = 0, $initialize = true)
'atom' => $scripturl . '?action=.xml;type=atom;limit=' . (!empty($modSettings['xmlnews_limit']) ? $modSettings['xmlnews_limit'] : 5)
);
+ if (!empty($_SESSION['agreement_accepted']))
+ {
+ // This loadLanguage is needed here because the check is done way too early in the process.
+ // As a stop-gap it's fine, but in future versions it should be fixed somehow.
+ $_SESSION['agreement_accepted'] = null;
+ $context['accepted_agreement'] = array(
+ 'errors' => array(
+ 'accepted_agreement' => $txt['agreement_accepted']
+ )
+ );
+ }
+
theme()->loadThemeJavascript();
Hooks::instance()->newPath(array('$themedir' => $settings['theme_dir']));
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index 4d8b8ceebd..d8ff9a892d 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -1248,8 +1248,9 @@ public function action_agreement()
if (isset($this->_req->post->accept_agreement))
{
- require_once(SUBSDIR . '/Members.subs.php');
- registerAgreementAccepted($user_info['id'], $user_info['ip'], empty($modSettings['agreementRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['agreementRevision']);
+ $agreement = new Agreement($user_info['language']);
+ $agreement->accept($user_info['id'], $user_info['ip'], empty($modSettings['agreementRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['agreementRevision']);
+
$_SESSION['agreement_accepted'] = true;
if (isset($_SESSION['agreement_url_redirect']))
{
diff --git a/sources/subs/Agreement.class.php b/sources/subs/Agreement.class.php
index b3b82d53ed..672e7677fd 100644
--- a/sources/subs/Agreement.class.php
+++ b/sources/subs/Agreement.class.php
@@ -32,6 +32,27 @@ class Agreement
*/
protected $_backup_dir = '';
+ /**
+ * The name of the file where the agreement is stored
+ *
+ * @var string
+ */
+ protected $_file_name = 'agreement';
+
+ /**
+ * The name of the directory where the backup will be saved
+ *
+ * @var string
+ */
+ protected $_backupdir_name = 'agreements';
+
+ /**
+ * The name of the log table
+ *
+ * @var string
+ */
+ protected $_log_table_name = '{db_prefix}log_agreement_accept';
+
/**
* The database object
*
@@ -50,7 +71,7 @@ public function __construct($language, $backup_dir = null)
$this->_language = strtr($language, array('.' => ''));
if ($backup_dir === null || file_exists($backup_dir) === false)
{
- $backup_dir = BOARDDIR . '/packages/backups/agreements';
+ $backup_dir = BOARDDIR . '/packages/backups/' . $this->_backupdir_name;
}
$this->_backup_dir = $backup_dir;
$this->_db = database();
@@ -77,7 +98,7 @@ public function save($text, $update_backup = false)
}
// Off it goes to the agreement file.
- $fp = fopen(BOARDDIR . '/agreement' . $this->normalizeLanguage() . '.txt', 'w');
+ $fp = fopen(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt', 'w');
fwrite($fp, str_replace("\r", '', $text));
fclose($fp);
@@ -96,13 +117,13 @@ public function save($text, $update_backup = false)
public function getPlainText($fallback = true)
{
// Have we got a localized one?
- if (file_exists(BOARDDIR . '/agreement' . $this->normalizeLanguage() . '.txt'))
+ if (file_exists(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt'))
{
- $agreement = file_get_contents(BOARDDIR . '/agreement' . $this->normalizeLanguage() . '.txt');
+ $agreement = file_get_contents(BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt');
}
- elseif ($fallback === true && file_exists(BOARDDIR . '/agreement.txt'))
+ elseif ($fallback === true && file_exists(BOARDDIR . '/' . $this->_file_name . '.txt'))
{
- $agreement = file_get_contents(BOARDDIR . '/agreement.txt');
+ $agreement = file_get_contents(BOARDDIR . '/' . $this->_file_name . '.txt');
}
else
{
@@ -134,7 +155,7 @@ public function getParsedText($fallback = true)
*/
public function isWritable()
{
- $filename = BOARDDIR . '/agreement' . $this->normalizeLanguage() . '.txt';
+ $filename = BOARDDIR . '/' . $this->_file_name . $this->normalizeLanguage() . '.txt';
return file_exists($filename) && is_writable($filename);
}
@@ -143,24 +164,48 @@ public function isWritable()
* Test if the user accepted the current agreement or not.
*
* @param int $id_member The id of the member
- * @param string $agreement_date The date of the agreement
+ * @param string $version The date of the agreement
*/
- public function checkAccepted($id_member, $agreement_date)
+ public function checkAccepted($id_member, $version)
{
$accepted = $this->_db->fetchQuery('
SELECT 1
- FROM {db_prefix}log_agreement_accept
- WHERE agreement_date = {date:agreement_date}
+ FROM ' . $this->_log_table_name . '
+ WHERE version = {date:version}
AND id_member = {int:id_member}',
array(
'id_member' => $id_member,
- 'agreement_date' => $agreement_date,
+ 'version' => $version,
)
);
return !empty($accepted);
}
+ public function accept($id_member, $ip, $version)
+ {
+ $db = database();
+
+ $db->insert('',
+ $this->_log_table_name,
+ array(
+ 'version' => 'date',
+ 'id_member' => 'int',
+ 'accepted_date' => 'date',
+ 'accepted_ip' => 'string-255',
+ ),
+ array(
+ array(
+ 'version' => $version,
+ 'id_member' => $id_member,
+ 'accepted_date' => strftime('%Y-%m-%d', forum_time(false)),
+ 'accepted_ip' => $ip,
+ )
+ ),
+ array('version', 'id_member')
+ );
+ }
+
/**
* Takes care of the edge-case of the default agreement that doesn't have
* the language in the name, and the fact that the admin panels loads it
@@ -188,7 +233,7 @@ protected function _createBackup($backup_id)
{
return false;
}
- $glob = new GlobIterator(BOARDDIR . '/agreement*.txt', FilesystemIterator::SKIP_DOTS);
+ $glob = new GlobIterator(BOARDDIR . '/' . $this->_file_name . '*.txt', FilesystemIterator::SKIP_DOTS);
foreach ($glob as $file)
{
copy($file->getPathname(), $destination . $file->getBasename());
diff --git a/sources/subs/Members.subs.php b/sources/subs/Members.subs.php
index b271b90e13..6ac9bd38d0 100644
--- a/sources/subs/Members.subs.php
+++ b/sources/subs/Members.subs.php
@@ -2615,19 +2615,19 @@ function registerAgreementAccepted($id_member, $ip, $agreement_version)
$db->insert('',
'{db_prefix}log_agreement_accept',
array(
- 'agreement_date' => 'date',
+ 'version' => 'date',
'id_member' => 'int',
'accepted_date' => 'date',
'accepted_ip' => 'string-255',
),
array(
array(
- 'agreement_date' => $agreement_version,
+ 'version' => $agreement_version,
'id_member' => $id_member,
'accepted_date' => strftime('%Y-%m-%d', forum_time(false)),
'accepted_ip' => $ip,
)
),
- array('agreement_date', 'id_member')
+ array('version', 'id_member')
);
-}
\ No newline at end of file
+}
From 5445dd55ac07c7b5fec16c41cb1a7a07d6d9af1f Mon Sep 17 00:00:00 2001
From: emanuele
Date: Thu, 24 May 2018 21:36:24 +0200
Subject: [PATCH 06/13] Add the admin interface to edit the privacy policy
---
install/install_1-1.php | 4 +-
install/upgrade_1-1.php | 19 +++-
sources/admin/Admin.controller.php | 1 +
.../admin/ManageRegistration.controller.php | 93 +++++++++++++++++++
sources/controllers/Register.controller.php | 44 +++++++++
sources/subs/Agreement.class.php | 23 ++++-
sources/subs/PrivacyPolicy.class.php | 35 +++++++
themes/default/Register.template.php | 19 ++--
.../languages/english/Admin.english.php | 9 +-
.../languages/english/Login.english.php | 3 +-
themes/default/scripts/admin.js | 7 ++
11 files changed, 240 insertions(+), 17 deletions(-)
create mode 100644 sources/subs/PrivacyPolicy.class.php
diff --git a/install/install_1-1.php b/install/install_1-1.php
index 4ede073943..e3cca67adf 100644
--- a/install/install_1-1.php
+++ b/install/install_1-1.php
@@ -1157,7 +1157,7 @@ public function table_log_agreement_accept()
{
return $this->table->db_create_table('{db_prefix}log_agreement_accept',
array(
- array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'version', 'type' => 'varchar', 'size' => 20, 'default' => ''),
array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
@@ -2425,7 +2425,7 @@ public function table_log_privacy_policy_accept()
{
return $this->table->db_create_table('{db_prefix}log_privacy_policy_accept',
array(
- array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'version', 'type' => 'varchar', 'size' => 20, 'default' => ''),
array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
diff --git a/install/upgrade_1-1.php b/install/upgrade_1-1.php
index 1d8da34fa2..3d39787c36 100644
--- a/install/upgrade_1-1.php
+++ b/install/upgrade_1-1.php
@@ -868,19 +868,32 @@ public function update_settings()
public function agreement_logging_title()
{
- return 'Introducing the logging of accepted agreement...';
+ return 'Introducing the logging of accepted agreement and privacy policy...';
}
public function agreement_logging()
{
return array(
array(
- 'debug_title' => 'Creating the table...',
+ 'debug_title' => 'Creating the tables...',
'function' => function()
{
$this->table->db_create_table('{db_prefix}log_agreement_accept',
array(
- array('name' => 'version', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'version', 'type' => 'varchar', 'size' => 20, 'default' => ''),
+ array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
+ array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
+ array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
+ ),
+ array(
+ array('name' => 'version', 'columns' => array('version', 'id_member'), 'type' => 'primary'),
+ ),
+ array(),
+ 'ignore'
+ );
+ $this->table->db_create_table('{db_prefix}log_privacy_policy_accept',
+ array(
+ array('name' => 'version', 'type' => 'varchar', 'size' => 20, 'default' => ''),
array('name' => 'id_member', 'type' => 'mediumint', 'size' => 10, 'unsigned' => true, 'default' => 0),
array('name' => 'accepted_date', 'type' => 'date', 'default' => '0001-01-01'),
array('name' => 'accepted_ip', 'type' => 'varchar', 'size' => 255, 'default' => ''),
diff --git a/sources/admin/Admin.controller.php b/sources/admin/Admin.controller.php
index 5cf25d1058..183346bc96 100644
--- a/sources/admin/Admin.controller.php
+++ b/sources/admin/Admin.controller.php
@@ -457,6 +457,7 @@ private function loadMenu()
'subsections' => array(
'register' => array($txt['admin_browse_register_new'], 'moderate_forum'),
'agreement' => array($txt['registration_agreement'], 'admin_forum'),
+ 'privacypol' => array($txt['privacy_policy'], 'admin_forum'),
'reservednames' => array($txt['admin_reserved_set'], 'admin_forum'),
'settings' => array($txt['settings'], 'admin_forum'),
),
diff --git a/sources/admin/ManageRegistration.controller.php b/sources/admin/ManageRegistration.controller.php
index 81c8603a5e..f76bb32b46 100644
--- a/sources/admin/ManageRegistration.controller.php
+++ b/sources/admin/ManageRegistration.controller.php
@@ -62,6 +62,11 @@ public function action_index()
'function' => 'action_agreement',
'permission' => 'admin_forum',
),
+ 'privacypol' => array(
+ 'controller' => $this,
+ 'function' => 'action_privacypol',
+ 'permission' => 'admin_forum',
+ ),
'reservednames' => array(
'controller' => $this,
'function' => 'action_reservednames',
@@ -89,6 +94,9 @@ public function action_index()
'agreement' => array(
'description' => $txt['registration_agreement_desc'],
),
+ 'privacypol' => array(
+ 'description' => $txt['privacy_policy_desc'],
+ ),
'reservednames' => array(
'description' => $txt['admin_reserved_desc'],
),
@@ -303,10 +311,95 @@ public function action_agreement()
$context['checkbox_agreement'] = !empty($modSettings['checkboxAgreement']);
$context['sub_template'] = 'edit_agreement';
+ $context['subaction'] = 'agreement';
+ $context['agreement_show_options'] = true;
$context['page_title'] = $txt['registration_agreement'];
createToken('admin-rega');
}
+ /**
+ * Allows the administrator to edit the privacy policy, and choose whether
+ * it should be shown or not.
+ *
+ * - It writes and saves the privacy policy to the privacypol.txt file.
+ * - Accessed by ?action=admin;area=regcenter;sa=privacypol.
+ * - Requires the admin_forum permission.
+ *
+ * @uses Admin template and the edit_agreement sub template.
+ */
+ public function action_privacypol()
+ {
+ // I hereby agree not to be a lazy bum.
+ global $txt, $context, $modSettings;
+
+ // By default we look at privacypol.txt.
+ $context['current_agreement'] = '';
+
+ // Is there more than one to edit?
+ $context['editable_agreements'] = array(
+ '' => $txt['admin_agreement_default'],
+ );
+
+ // Get our languages.
+ $languages = getLanguages();
+
+ // Try to figure out if we have more agreements.
+ foreach ($languages as $lang)
+ {
+ if (file_exists(BOARDDIR . '/privacypol.' . $lang['filename'] . '.txt'))
+ {
+ $context['editable_agreements'][$lang['filename']] = $lang['name'];
+
+ // Are we editing this?
+ if (isset($this->_req->post->agree_lang) && $this->_req->post->agree_lang === $lang['filename'])
+ {
+ $context['current_agreement'] = $lang['filename'];
+ break;
+ }
+ }
+ }
+
+ $context['warning'] = '';
+ $privacypol = new \PrivacyPolicy($context['current_agreement']);
+
+ if (isset($this->_req->post->save) && isset($this->_req->post->agreement))
+ {
+ checkSession();
+ validateToken('admin-rega');
+
+ // Off it goes to the agreement file.
+ $success = $privacypol->save($this->_req->post->agreement, !empty($this->_req->post->checkboxAcceptAgreement));
+ if (!empty($this->_req->post->checkboxAcceptAgreement))
+ {
+ if ($success === false)
+ {
+ $context['warning'] .= $txt['privacypol_backup_not_writable'] . ' ';
+ }
+ else
+ {
+ updateSettings(array('agreementRevision' => $success));
+ }
+ }
+
+ updateSettings(array('requirePrivacypolicy' => !empty($this->_req->post->requireAgreement), 'checkboxPrivacypolicy' => !empty($this->_req->post->checkboxAgreement)));
+ }
+
+ $context['agreement'] = Util::htmlspecialchars($privacypol->getPlainText(false));
+
+ $context['warning'] .= $privacypol->isWritable() ? '' : $txt['privacypol_not_writable'];
+ $context['require_agreement'] = !empty($modSettings['requirePrivacypolicy']);
+ $context['checkbox_agreement'] = !empty($modSettings['checkboxPrivacypolicy']);
+
+ $context['sub_template'] = 'edit_agreement';
+ $context['subaction'] = 'privacypol';
+ $context['page_title'] = $txt['privacy_policy'];
+ // These overrides are here to be able to reuse the template in a simple way without having to change much.
+ $txt['admin_agreement'] = $txt['admin_privacypol'];
+ $txt['admin_checkbox_accept_agreement'] = $txt['admin_checkbox_accept_privacypol'];
+
+ createToken('admin-rega');
+ }
+
/**
* Set the names under which users are not allowed to register.
*
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index d8ff9a892d..0fc0c28f75 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -84,6 +84,7 @@ public function action_index()
'verificationcode' => array($this, 'action_verificationcode'),
'coppa' => array($this, 'action_coppa'),
'agrelang' => array($this, 'action_agrelang'),
+ 'privacypol' => array($this, 'action_privacypol'),
'agreement' => array($this, 'action_agreement'),
);
@@ -1285,4 +1286,47 @@ public function action_agreement()
}
createToken('register');
}
+
+ public function action_privacypol()
+ {
+ global $context, $user_info, $modSettings;
+
+ $policy = new \PrivacyPolicy($user_info['language']);
+
+ if (isset($this->_req->post->accept_agreement))
+ {
+ $policy->accept($user_info['id'], $user_info['ip'], empty($modSettings['privacyPolicyRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['privacyPolicyRevision']);
+
+ $_SESSION['privacypolicy_accepted'] = true;
+ if (isset($_SESSION['privacypolicy_url_redirect']))
+ {
+ redirectexit($_SESSION['privacypolicy_url_redirect']);
+ }
+ }
+ elseif (isset($this->_req->post->no_accept))
+ {
+ redirectexit('action=profile;area=deleteaccount');
+ }
+ else
+ {
+ $context['sub_template'] = 'registration_agreement';
+ $context['register_subaction'] = 'privacypol';
+ }
+
+ loadLanguage('Login');
+ loadLanguage('Profile');
+ loadTemplate('Register');
+
+ $txt['agreement_agree'] = $txt['policy_agree'];
+ $txt['agreement_no_agree'] = $txt['policy_no_agree'];
+
+ // If you have to agree to the privacy policy, it needs to be fetched from the file.
+ $context['agreement'] = $policy->getParsedText();
+
+ $context['show_coppa'] = false;
+ $context['skip_coppa'] = true;
+ $context['show_contact_button'] = !empty($modSettings['enable_contactform']) && $modSettings['enable_contactform'] === 'registration';
+
+ createToken('register');
+ }
}
diff --git a/sources/subs/Agreement.class.php b/sources/subs/Agreement.class.php
index 672e7677fd..c2781dfdc4 100644
--- a/sources/subs/Agreement.class.php
+++ b/sources/subs/Agreement.class.php
@@ -90,7 +90,7 @@ public function save($text, $update_backup = false)
$backup_id = '';
if ($update_backup === true)
{
- $backup_id = strftime('%Y-%m-%d', forum_time(false));
+ $backup_id = $this->_backupId();
if ($this->_createBackup($backup_id) === false)
{
$backup_id = false;
@@ -171,7 +171,7 @@ public function checkAccepted($id_member, $version)
$accepted = $this->_db->fetchQuery('
SELECT 1
FROM ' . $this->_log_table_name . '
- WHERE version = {date:version}
+ WHERE version = {string:version}
AND id_member = {int:id_member}',
array(
'id_member' => $id_member,
@@ -189,7 +189,7 @@ public function accept($id_member, $ip, $version)
$db->insert('',
$this->_log_table_name,
array(
- 'version' => 'date',
+ 'version' => 'string-20',
'id_member' => 'int',
'accepted_date' => 'date',
'accepted_ip' => 'string-255',
@@ -216,6 +216,21 @@ protected function normalizeLanguage()
return $this->_language === '' ? '' : '.' . $this->_language;
}
+ protected function _backupId()
+ {
+ $backup_id = strftime('%Y-%m-%d', forum_time(false));
+ $counter = '';
+ $merger = '';
+
+ while (file_exists($this->_backup_dir . '/' . $backup_id . $merger . $counter . '/') === true)
+ {
+ $counter++;
+ $merger = '_';
+ }
+
+ return $backup_id . $merger . $counter;
+ }
+
/**
* Creates a full backup of all the agreements.
*
@@ -240,4 +255,4 @@ protected function _createBackup($backup_id)
}
return true;
}
-}
\ No newline at end of file
+}
diff --git a/sources/subs/PrivacyPolicy.class.php b/sources/subs/PrivacyPolicy.class.php
new file mode 100644
index 0000000000..8109bf12af
--- /dev/null
+++ b/sources/subs/PrivacyPolicy.class.php
@@ -0,0 +1,35 @@
+_log_table_name = '{db_prefix}log_privacy_policy_accept';
+ $this->_backupdir_name = 'privacypolicies';
+ $this->_file_name = 'privacypolicy';
+
+ parent::__construct($language, $backup_dir);
+ }
+}
\ No newline at end of file
diff --git a/themes/default/Register.template.php b/themes/default/Register.template.php
index 12a0fff6f5..693c0c2fe4 100644
--- a/themes/default/Register.template.php
+++ b/themes/default/Register.template.php
@@ -733,8 +733,8 @@ function template_edit_agreement()
// Just a big box to edit the text file ;).
echo '
-
diff --git a/themes/default/languages/english/Admin.english.php b/themes/default/languages/english/Admin.english.php
index 4ca2ccd2ed..30670399ae 100644
--- a/themes/default/languages/english/Admin.english.php
+++ b/themes/default/languages/english/Admin.english.php
@@ -47,11 +47,14 @@
$txt['admin_credits'] = 'Credits';
$txt['admin_agreement'] = 'Show and require agreement letter when registering';
$txt['admin_checkbox_agreement'] = 'Show a checkbox for the agreement in registration form instead of a full page';
-$txt['admin_checkbox_accept_agreement'] = 'Force members to accept the agreement at the next login';
+$txt['admin_checkbox_accept_agreement'] = 'Force all members to accept the agreement at the next visit to the forum';
$txt['admin_agreement_default'] = 'Default';
$txt['admin_agreement_select_language'] = 'Language to edit';
$txt['admin_agreement_select_language_change'] = 'Change';
+$txt['admin_privacypol'] = 'Require to accept the privacy policy when registering';
+$txt['admin_checkbox_accept_privacypol'] = 'Force all members to accept the privacy policy at the next visit to the forum';
+
$txt['admin_delete_members'] = 'Delete Selected Members';
$txt['admin_change_primary_membergroup'] = 'Change primary member group';
$txt['admin_change_secondary_membergroup'] = 'Change/add additional member group';
@@ -88,6 +91,8 @@
$txt['database_name'] = 'Database Name';
$txt['registration_agreement'] = 'Registration Agreement';
$txt['registration_agreement_desc'] = 'This agreement is shown when a user registers an account on this forum and has to be accepted before users can continue registration.';
+$txt['privacy_policy'] = 'Privacy Policy';
+$txt['privacy_policy_desc'] = 'This privacy policy is shown when a user registers an account on this forum and can be made mandatory before users can continue registration.';
$txt['database_prefix'] = 'Database Tables Prefix';
$txt['errors_list'] = 'Listing of forum errors';
$txt['errors_found'] = 'The following errors are fouling up your forum';
@@ -126,6 +131,8 @@
$txt['remove_all'] = 'Clear Log';
$txt['agreement_not_writable'] = 'Warning - agreement.txt is not writable, any changes you make will NOT be saved.';
$txt['agreement_backup_not_writable'] = 'Warning - the backup directory in forum_root/packages/backup cannot be created.';
+$txt['privacypol_not_writable'] = 'Warning - privacypol.txt is not writable, any changes you make will NOT be saved.';
+$txt['privacypol_backup_not_writable'] = 'Warning - the backup directory in forum_root/packages/backup cannot be created.';
$txt['version_check_desc'] = 'This shows you the versions of your installation\'s files versus those of the latest version. If any of these files are out of date, you should download and upgrade to the latest version at our ElkArte Site.';
$txt['version_check_more'] = '(more detailed)';
diff --git a/themes/default/languages/english/Login.english.php b/themes/default/languages/english/Login.english.php
index a0c7b2d687..0f4314d71d 100644
--- a/themes/default/languages/english/Login.english.php
+++ b/themes/default/languages/english/Login.english.php
@@ -29,6 +29,7 @@
$txt['login_below'] = 'Please login below.';
$txt['login_below_or_register'] = 'Please login below or register an account with %2$s';
$txt['checkbox_agreement'] = 'I accept the registration agreement';
+$txt['confirm_request_accept_agreement'] = 'Are you sure you want to force all the users to accept the agreement?';
$txt['login_hash_error'] = 'Password security has recently been upgraded. Please enter your password again.';
@@ -148,4 +149,4 @@
$txt['contact_your_message'] = 'Your message';
$txt['errors_contact_form'] = 'The following errors occurred while processing your contact request';
$txt['contact_subject'] = 'A guest has sent you a message';
-$txt['contact_thankyou'] = 'Thank you for your message, someone will contact you as soon as possible.';
\ No newline at end of file
+$txt['contact_thankyou'] = 'Thank you for your message, someone will contact you as soon as possible.';
diff --git a/themes/default/scripts/admin.js b/themes/default/scripts/admin.js
index a105a99a2e..bbaf17a49f 100644
--- a/themes/default/scripts/admin.js
+++ b/themes/default/scripts/admin.js
@@ -2004,3 +2004,10 @@ $(function() {
});
});
});
+
+function confirmAgreement(text) {
+ if ($('#checkboxAcceptAgreement').is(':checked')) {
+ return confirm(text);
+ }
+ return true;
+}
From 765040204158803605ed873cf26c51f7b09b237f Mon Sep 17 00:00:00 2001
From: emanuele
Date: Thu, 24 May 2018 23:58:15 +0200
Subject: [PATCH 07/13] And finally binds the registration to the acceptance of
both the agreement and the privacy policy (if present)
---
.../admin/ManageRegistration.controller.php | 3 +-
sources/controllers/Jslocale.controller.php | 13 ++++-
sources/controllers/Register.controller.php | 44 ++++++++++++++-
themes/default/Register.template.php | 54 +++++++++++++++----
themes/default/css/index.css | 4 +-
.../languages/english/Errors.english.php | 3 +-
.../languages/english/Login.english.php | 4 ++
themes/default/scripts/register.js | 5 +-
8 files changed, 109 insertions(+), 21 deletions(-)
diff --git a/sources/admin/ManageRegistration.controller.php b/sources/admin/ManageRegistration.controller.php
index f76bb32b46..3ce6c98902 100644
--- a/sources/admin/ManageRegistration.controller.php
+++ b/sources/admin/ManageRegistration.controller.php
@@ -381,14 +381,13 @@ public function action_privacypol()
}
}
- updateSettings(array('requirePrivacypolicy' => !empty($this->_req->post->requireAgreement), 'checkboxPrivacypolicy' => !empty($this->_req->post->checkboxAgreement)));
+ updateSettings(array('requirePrivacypolicy' => !empty($this->_req->post->requireAgreement)));
}
$context['agreement'] = Util::htmlspecialchars($privacypol->getPlainText(false));
$context['warning'] .= $privacypol->isWritable() ? '' : $txt['privacypol_not_writable'];
$context['require_agreement'] = !empty($modSettings['requirePrivacypolicy']);
- $context['checkbox_agreement'] = !empty($modSettings['checkboxPrivacypolicy']);
$context['sub_template'] = 'edit_agreement';
$context['subaction'] = 'privacypol';
diff --git a/sources/controllers/Jslocale.controller.php b/sources/controllers/Jslocale.controller.php
index c2c5e35d5f..a7467c7fbb 100644
--- a/sources/controllers/Jslocale.controller.php
+++ b/sources/controllers/Jslocale.controller.php
@@ -87,9 +87,18 @@ public function action_agreement_api()
{
// If you have to agree to the agreement, it needs to be fetched from the file.
$agreement = new \Agreement($lang);
+ if (!empty($modSettings['requirePrivacypolicy']))
+ {
+ $privacypol = new \PrivacyPolicy($lang);
+ }
+ $context['json_data'] = array('agreement' => '', 'privacypol' => '');
try
{
- $context['json_data'] = $agreement->getParsedText();
+ $context['json_data']['agreement'] = $agreement->getParsedText();
+ if (!empty($modSettings['requirePrivacypolicy']))
+ {
+ $context['json_data']['privacypol'] = $privacypol->getParsedText();
+ }
}
catch (\Elk_Exception $e)
{
@@ -133,4 +142,4 @@ private function _sendFile()
// And terminate
obExit(false);
}
-}
\ No newline at end of file
+}
diff --git a/sources/controllers/Register.controller.php b/sources/controllers/Register.controller.php
index 0fc0c28f75..0fa1b99384 100644
--- a/sources/controllers/Register.controller.php
+++ b/sources/controllers/Register.controller.php
@@ -133,7 +133,9 @@ public function action_register()
// Do we need them to agree to the registration agreement, first?
$context['require_agreement'] = !empty($modSettings['requireAgreement']);
$context['checkbox_agreement'] = !empty($modSettings['checkboxAgreement']);
+ $context['require_privacypol'] = !empty($modSettings['requirePrivacypolicy']);
$context['registration_passed_agreement'] = !empty($_SESSION['registration_agreed']);
+ $context['registration_passed_privacypol'] = !empty($_SESSION['registration_privacypolicy']);
$context['show_coppa'] = !empty($modSettings['coppaAge']);
$context['show_contact_button'] = !empty($modSettings['enable_contactform']) && $modSettings['enable_contactform'] === 'registration';
if (!empty($modSettings['show_DisplayNameOnRegistration']))
@@ -161,6 +163,7 @@ public function action_register()
if ($current_step == 1 && (isset($this->_req->post->accept_agreement) || isset($this->_req->post->accept_agreement_coppa)))
{
$context['registration_passed_agreement'] = $_SESSION['registration_agreed'] = true;
+ $context['registration_passed_privacypol'] = $_SESSION['registration_privacypolicy'] = true;
$current_step = 2;
// Skip the coppa procedure if the user says he's old enough.
@@ -224,6 +227,23 @@ public function action_register()
throw new Elk_Exception('registration_disabled', false);
}
+ if (!empty($context['require_privacypol']))
+ {
+ $privacypol = new \PrivacyPolicy($user_info['language']);
+ $context['privacy_policy'] = $privacypol->getParsedText();
+
+ if (empty($context['privacy_policy']))
+ {
+ // No file found or a blank file, log the error so the admin knows there is a problem!
+ loadLanguage('Errors');
+ Errors::instance()->log_error($txt['registration_privacy_policy_missing'], 'critical');
+ throw new Elk_Exception('registration_disabled', false);
+ }
+ }
+
+ // If we have language support enabled then they need to be loaded
+ $this->_load_language_support();
+
// Any custom or standard profile fields we want filled in during registration?
$this->_load_profile_fields();
@@ -289,6 +309,13 @@ public function action_register2()
if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed']))
redirectexit();
+ if (!empty($modSettings['requireAgreement']) && !empty($modSettings['requirePrivacypolicy']) && !empty($this->_req->post->checkbox_privacypol))
+ $_SESSION['registration_privacypolicy'] = true;
+
+ // Well, if you don't agree, you can't register.
+ if (!empty($modSettings['requireAgreement']) && !empty($modSettings['requirePrivacypolicy']) && empty($_SESSION['registration_privacypolicy']))
+ redirectexit();
+
// Make sure they came from *somewhere*, have a session.
if (!isset($_SESSION['old_url']))
redirectexit('action=register');
@@ -492,6 +519,16 @@ public function do_register($verifiedOpenID = false)
$regOptions['ip2'] = $req->ban_ip();
$memberID = registerMember($regOptions, 'register');
+ $lang = !empty($modSettings['userLanguage']) ? $modSettings['userLanguage'] : 'english';
+ $agreement = new Agreement($lang);
+ $agreement->accept($memberID, $user_info['ip'], empty($modSettings['agreementRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['agreementRevision']);
+
+ if (!empty($modSettings['requirePrivacypolicy']))
+ {
+ $policy = new \PrivacyPolicy($lang);
+ $policy->accept($memberID, $user_info['ip'], empty($modSettings['privacyPolicyRevision']) ? strftime('%Y-%m-%d', forum_time(false)) : $modSettings['privacyPolicyRevision']);
+ }
+
// If there are "important" errors and you are not an admin: log the first error
// Otherwise grab all of them and don't log anything
if ($reg_errors->hasErrors(1) && !$user_info['is_admin'])
@@ -1245,7 +1282,7 @@ public function action_registerCheckUsername()
public function action_agreement()
{
- global $context, $user_info, $modSettings;
+ global $context, $user_info, $modSettings, $txt;
if (isset($this->_req->post->accept_agreement))
{
@@ -1274,6 +1311,7 @@ public function action_agreement()
// If you have to agree to the agreement, it needs to be fetched from the file.
$agreement = new \Agreement($user_info['language']);
$context['agreement'] = $agreement->getParsedText();
+ $context['page_title'] = $txt['registration_agreement'];
$context['show_coppa'] = !empty($modSettings['coppaAge']);
$context['show_contact_button'] = !empty($modSettings['enable_contactform']) && $modSettings['enable_contactform'] === 'registration';
@@ -1289,7 +1327,7 @@ public function action_agreement()
public function action_privacypol()
{
- global $context, $user_info, $modSettings;
+ global $context, $user_info, $modSettings, $txt;
$policy = new \PrivacyPolicy($user_info['language']);
@@ -1319,6 +1357,8 @@ public function action_privacypol()
$txt['agreement_agree'] = $txt['policy_agree'];
$txt['agreement_no_agree'] = $txt['policy_no_agree'];
+ $txt['registration_agreement'] = $txt['registration_privacy_policy'];
+ $context['page_title'] = $txt['registration_agreement'];
// If you have to agree to the privacy policy, it needs to be fetched from the file.
$context['agreement'] = $policy->getParsedText();
diff --git a/themes/default/Register.template.php b/themes/default/Register.template.php
index 693c0c2fe4..1eecac82e6 100644
--- a/themes/default/Register.template.php
+++ b/themes/default/Register.template.php
@@ -21,34 +21,54 @@ function template_registration_agreement()
global $context, $scripturl, $txt;
echo '
-