Commit 44a6284e authored by Maria Sorica's avatar Maria Sorica

Bug 1734174: Add the after login privacy page

Upon login, if the user has not yet agreed to the most
recent Privacy statement versions, he will be redirected
to this page.

On install admin user accepts default privacy

behatnotneeded

Change-Id: I6afc3d4d4db0676782a8b1501a962862108eab6b
parent ae6c3fd9
......@@ -751,6 +751,61 @@ function auth_get_available_auth_types($institution=null) {
function auth_check_required_fields() {
global $USER, $SESSION;
// for the case we are mascarading as the user and we want to return to be admin user
$restoreadmin = param_integer('restore', 0);
// Privacy statement.
if (get_config('institutionstrictprivacy') && !$USER->has_latest_agreement() && !$restoreadmin) {
// Get all institutions of a user.
$userinstitutions = array_keys($USER->get('institutions'));
// Include the 'mahara' institution so that we may show the site privacy statement as well.
array_push($userinstitutions, 'mahara');
// Check if there are new privacies that need to be accepted.
$latestversions = get_latest_privacy_versions($userinstitutions, true);
foreach ($latestversions as $privacy) {
$elements[$privacy->institution . 'text'] = array(
'type' => 'markup',
'value' => '<h2>' . ($privacy->institution == 'mahara' ? get_string('siteprivacystatement', 'admin') : get_string('institutionprivacystatement', 'admin')) . '</h2>' . $privacy->content,
);
$elements[$privacy->institution . 'id'] = array(
'type' => 'hidden',
'value' => $privacy->id,
);
$elements[$privacy->institution] = array(
'type' => 'switchbox',
'title' => get_string('privacyagreement', 'admin'),
'description' => $privacy->agreed ? get_string('privacyagreedto', 'admin', format_date(strtotime($privacy->agreedtime))) : '',
'defaultvalue' => $privacy->agreed ? true : false,
'disabled' => $privacy->agreed ? true : false,
'required' => true,
);
$elements[$privacy->institution . 'switch'] = array(
'type' => 'hidden',
'value' => $privacy->agreed ? 'disabled' : 'enabled',
);
}
$elements['submit'] = array(
'class' => 'btn-primary',
'type' => 'submit',
'value' => get_string('savechanges', 'admin')
);
$form = pieform(array(
'name' => 'agreetoprivacy',
'jsform' => false,
'jssuccesscallback' => 'contentSaved',
'elements' => $elements,
));
define('TITLE', get_string('privacy', 'admin'));
$smarty = smarty();
setpageicon($smarty, 'icon-umbrella');
$smarty->assign('form', $form);
$smarty->display('account/useracceptprivacy.tpl');
exit;
}
if (defined('NOCHECKREQUIREDFIELDS') || $SESSION->get('nocheckrequiredfields') === true) {
return;
}
......@@ -1137,6 +1192,29 @@ function requiredfields_submit(Pieform $form, $values) {
redirect();
}
function agreetoprivacy_submit(Pieform $form, $values) {
global $USER, $SESSION;
$userinstitutions = array_keys($USER->get('institutions'));
array_push($userinstitutions, 'mahara');
foreach ($userinstitutions as $institution) {
if ($values[$institution . 'switch'] == 'disabled') {
continue;
}
$agreed = (empty($values[$institution]) ? 0 : $values[$institution]);
try {
save_user_reply_to_agreement($USER->get('id'), $values[$institution . 'id'], $agreed);
$SESSION->add_ok_msg(get_string('agreementsaved', 'admin'));
}
catch (SQLException $e) {
$SESSION->add_ok_msg(get_string('savefailed', 'admin'));
}
}
$SESSION->set('nocheckrequiredfields', true);
redirect();
}
/**
* Creates and displays the transient login page.
*
......
......@@ -11,6 +11,7 @@
defined('INTERNAL') || die();
define('MAXLOGINTRIES', 5);
require_once(get_config('docroot') . 'lib/user.php');
$put = array();
......@@ -607,6 +608,38 @@ class User {
return ($this->get('logout_time') > 0 ? true : false);
}
/**
* Determines if the user has accepted the latest Privacy statement
*
* @return boolean
*/
public function has_latest_agreement() {
global $USER;
// If users are logged in they cannot be logged out for site upgrade.
// We need to check if table exists otherwise we get error message about usr_agreement table
// not existing.
require_once('ddl.php');
if (!table_exists(new XMLDBTable("usr_agreement"))) {
return true;
}
$userinstitutions = array_keys($USER->get('institutions'));
// Include the 'mahara' institution so that we may show the site privacy statement as well.
array_push($userinstitutions, 'mahara');
// Check if there are new privacies that need to be accepted.
$latestversions = get_latest_privacy_versions($userinstitutions, true);
$hasagreement = true;
foreach ($latestversions as $key => $version) {
// Check if there are privacy statements the user needs to agree
if (!$version->agreed) {
$hasagreement = false;
}
}
return $hasagreement;
}
public function to_stdclass() {
$this->stdclass = new StdClass;
reset($this->defaults);
......
......@@ -1346,3 +1346,8 @@ $string['versionfor'] = 'Privacy statement for version "%s" is as follows:';
$string['institutionprivacystatement'] = 'Institution privacy statement';
$string['userprivacypagedescription'] = 'Displayed are the current privacy statements to which you consented.';
$string['lastupdated'] = 'Last updated on';
$string['newprivacy'] = 'Before entering your account, please read the privacy statement displayed below.';
$string['privacyagreement'] = 'I consent to this privacy statement';
$string['privacyagreementdescription'] = 'By choosing "Yes", you give your consent to the clauses of the privacy statement above.';
$string['privacyagreedto'] = 'You agreed to this privacy statement in %s.';
$string['agreementsaved'] = 'Agreement saved';
......@@ -5646,5 +5646,15 @@ function xmldb_core_upgrade($oldversion=0) {
create_table($table);
}
if ($oldversion < 2018013000) {
log_debug('Auto accept the privacy agreement for all site admins');
$sitecontentid = get_field('site_content_version', 'id', 'type', 'privacy', 'institution', 'mahara');
$admins = get_site_admins();
foreach ($admins as $admin) {
save_user_reply_to_agreement($admin->id, $sitecontentid, 1);
}
}
return $status;
}
......@@ -817,6 +817,9 @@ function core_install_lastcoredata_defaults() {
set_profile_field($user->id, 'email', $user->email);
set_profile_field($user->id, 'firstname', $user->firstname);
set_profile_field($user->id, 'lastname', $user->lastname);
// Accept the user privacy agreement on install
$sitecontentid = get_field('site_content_version', 'id', 'type', 'privacy', 'institution', 'mahara');
save_user_reply_to_agreement($user->id, $sitecontentid, 1);
handle_event('createuser', $user, array('password'));
activity_add_admin_defaults(array($user->id));
db_commit();
......
......@@ -3220,23 +3220,48 @@ function get_site_admins() {
}
/**
* Returns a list of the latest privacy statements of each institution the current user belongs to.
* Returns a list of the latest privacy statements of each institution the current user belongs to (including mahara).
*
* @param $institutions an array of the institutions to which the current user belongs to.
* @returns array of stdclass objects containing the latest privacy statements the user has agreed to.
* @param $ignoreagreevalue a boolean if true, get all the latest Privacy Statements of the institutions the user belongs to (including mahara)
* if false, get just the latest privacy statements the user has agreed to.
*
* @returns array of stdclass objects containing the latest privacy statements.
*/
function get_latest_privacy_versions($institutions = array()) {
function get_latest_privacy_versions($institutions = array(), $ignoreagreevalue = false) {
global $USER;
// Get the latest Privacy Statements the user has agreed to.
$joinsql = $ignoreagreevalue ? 'LEFT JOIN' : 'JOIN';
$latestversions = get_records_sql_assoc("
SELECT s.id, s.version, s.content, s.ctime, s.institution
SELECT s.id, s.version, s.content, s.ctime, s.institution, u.agreed, u.ctime AS agreedtime,
CASE s.institution WHEN 'mahara' THEN 1 ELSE 2 END as type
FROM {site_content_version} s
INNER JOIN (SELECT MAX(id) as current, institution
FROM {site_content_version}
GROUP BY institution) s2 ON s.institution = s2.institution AND s.id = s2.current
JOIN {usr_agreement} u ON s2.current = u.sitecontentid AND u.usr = ?
WHERE s.institution IN (" . join(',',array_map('db_quote',$institutions)) . ")", array($USER->get('id')));
{$joinsql} {usr_agreement} u ON s2.current = u.sitecontentid AND u.usr = ? AND u.agreed = 1
WHERE s.institution IN (" . join(',',array_map('db_quote',$institutions)) . ")
ORDER BY type", array($USER->get('id')));
return $latestversions;
}
/**
* Saves a user's reply to privacy agreement
*
*/
function save_user_reply_to_agreement($userid, $sitecontentid, $agreed) {
$usragreement = new StdClass;
$usragreement->usr = $userid;
$usragreement->sitecontentid = $sitecontentid;
$usragreement->ctime = db_format_timestamp(time());
$usragreement->agreed = $agreed;
if ($oldrecord = get_field('usr_agreement', 'id', 'sitecontentid', $sitecontentid, 'usr', $userid)) {
update_record('usr_agreement', $usragreement, array('id' => $oldrecord));
}
else {
insert_record('usr_agreement', $usragreement);
}
return true;
}
......@@ -16,7 +16,7 @@ $config = new stdClass();
// See https://wiki.mahara.org/wiki/Developer_Area/Version_Numbering_Policy
// For upgrades on stable branches, increment the version by one. On master, use the date.
$config->version = 2018011000;
$config->version = 2018013000;
$config->series = '18.04';
$config->release = '18.04dev';
$config->minupgradefrom = 2015030409;
......
......@@ -283,6 +283,14 @@ EOD;
require_once('activity.php');
activity_add_admin_defaults(array($user->id));
}
// Use the institution's privacy option if exists
$instprivacy = get_field('site_content_version', 'id', 'type', 'privacy', 'institution', $record['institution']);
$siteprivacy = get_field('site_content_version', 'id', 'type', 'privacy', 'institution', 'mahara');
// Accept the user privacy agreement
$sitecontentid = $instprivacy ? $instprivacy : $siteprivacy;
$agreed = !empty($record['agreement']) ? (bool)$record['agreement'] : 1; // accept by default
save_user_reply_to_agreement($user->id, $sitecontentid, $agreed);
if ($record['institution'] != 'mahara') {
if ($record['role'] == 'admin') {
......
{include file="header.tpl"}
<div class="lead">{str tag="newprivacy" section="admin"}</div>
<div>{$form|safe}</div>
{include file="footer.tpl"}
......@@ -57,39 +57,39 @@
{/if}
<div id="loading-box" class="loading-box hidden"></div>
</div>
<div class="nav-toggle-area">
{if $MAINNAV}
<button class="main-nav-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".nav-main" aria-expanded="false" aria-controls="nav-main" title='{str tag="mainmenu"}'>
<span class="sr-only">{str tag="showmainmenu"}</span>
<span class="icon icon-bars icon-lg" role="presentation" aria-hidden="true"></span>
</button>
{/if}
{if $MAINNAVADMIN}
<button class="admin-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".nav-main-admin" aria-expanded="false" aria-controls="nav-main-admin" title='{str tag="adminmenu"}'>
<span class="sr-only">{str tag="showadminmenu"}</span>
<span class="icon icon-wrench icon-large" role="presentation" aria-hidden="true"></span>
</button>
{/if}
{if $LOGGEDIN}
<a href="{profile_url($USER)}" class="user-icon" title='{str tag="profilepage"}'>
<img src="{profile_icon_url user=$USER maxheight=25 maxwidth=25}">
</a>
<button class="user-toggle navbar-toggle" role="button" data-toggle="collapse" data-target=".nav-main-user" aria-expanded="false" aria-controls="nav-main-user" title='{str tag="usermenu"}'>
<span class="sr-only">{str tag="showusermenu"}</span>
<span class="icon icon-chevron-down collapsed"></span>
<div class="nav-toggle-area">
{if $MAINNAV}
<button class="main-nav-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".nav-main" aria-expanded="false" aria-controls="nav-main" title='{str tag="mainmenu"}'>
<span class="sr-only">{str tag="showmainmenu"}</span>
<span class="icon icon-bars icon-lg" role="presentation" aria-hidden="true"></span>
</button>
{/if}
{if $MAINNAVADMIN}
<button class="admin-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".nav-main-admin" aria-expanded="false" aria-controls="nav-main-admin" title='{str tag="adminmenu"}'>
<span class="sr-only">{str tag="showadminmenu"}</span>
<span class="icon icon-wrench icon-large" role="presentation" aria-hidden="true"></span>
</button>
{/if}
{if $LOGGEDIN}
<a href="{profile_url($USER)}" class="user-icon" title='{str tag="profilepage"}'>
<img src="{profile_icon_url user=$USER maxheight=25 maxwidth=25}">
</a>
<button class="user-toggle navbar-toggle" role="button" data-toggle="collapse" data-target=".nav-main-user" aria-expanded="false" aria-controls="nav-main-user" title='{str tag="usermenu"}'>
<span class="sr-only">{str tag="showusermenu"}</span>
<span class="icon icon-chevron-down collapsed"></span>
</button>
{/if}
<!-- HIDE WHEN ON DESKTOP -->
{if !$nosearch && ($LOGGEDIN || $publicsearchallowed)}
<button class="search-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".navbar-form" aria-expanded="false" aria-controls="navbar-form">
<span class="icon icon-search icon-lg" role="presentation" aria-hidden="true"></span>
<span class="nav-title sr-only">{str tag="showsearch"}</span>
</button>
{/if}
<!-- HIDE WHEN ON DESKTOP -->
{if !$nosearch && ($LOGGEDIN || $publicsearchallowed)}
<button class="search-toggle navbar-toggle collapsed" role="button" data-toggle="collapse" data-target=".navbar-form" aria-expanded="false" aria-controls="navbar-form">
<span class="icon icon-search icon-lg" role="presentation" aria-hidden="true"></span>
<span class="nav-title sr-only">{str tag="showsearch"}</span>
</button>
{/if}
</div>
{/if}
</div>
{include file="header/topright.tpl"}
{include file="header/navigation.tpl"}
{include file="header/topright.tpl"}
{include file="header/navigation.tpl"}
</div>
</div>
</header>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment