This allows for adding Terms of Service documents to the database that
registered users need to accept before using the AUR. A revision field
can be used to indicate whether a document was updated. If it is
increased, all users are again asked to accept the new terms.

Signed-off-by: Lukas Fleischer <[email protected]>
---
 schema/aur-schema.sql     | 20 +++++++++++++
 upgrading/4.6.0.txt       | 20 +++++++++++++
 web/html/tos.php          | 50 ++++++++++++++++++++++++++++++++
 web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++++++++++++++++++++
 web/lib/aur.inc.php       | 23 +++++++++++++++
 web/lib/routing.inc.php   |  1 +
 6 files changed, 188 insertions(+)
 create mode 100644 web/html/tos.php

diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql
index e584165..45272bb 100644
--- a/schema/aur-schema.sql
+++ b/schema/aur-schema.sql
@@ -379,3 +379,23 @@ CREATE TABLE Bans (
        BanTS TIMESTAMP NOT NULL,
        PRIMARY KEY (IPAddress)
 ) ENGINE = InnoDB;
+
+-- Terms and Conditions
+--
+CREATE TABLE Terms (
+       ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+       Description VARCHAR(255) NOT NULL,
+       URL VARCHAR(8000) NOT NULL,
+       Revision INTEGER UNSIGNED NOT NULL DEFAULT 1,
+       PRIMARY KEY (ID)
+) ENGINE = InnoDB;
+
+-- Terms and Conditions accepted by users
+--
+CREATE TABLE AcceptedTerms (
+       UsersID INTEGER UNSIGNED NOT NULL,
+       TermsID INTEGER UNSIGNED NOT NULL,
+       Revision INTEGER UNSIGNED NOT NULL DEFAULT 0,
+       FOREIGN KEY (UsersID) REFERENCES Users(ID) ON DELETE CASCADE,
+       FOREIGN KEY (TermsID) REFERENCES Terms(ID) ON DELETE CASCADE
+) ENGINE = InnoDB;
diff --git a/upgrading/4.6.0.txt b/upgrading/4.6.0.txt
index b051bac..816409d 100644
--- a/upgrading/4.6.0.txt
+++ b/upgrading/4.6.0.txt
@@ -15,3 +15,23 @@ UPDATE PackageDepends
 ---
 ALTER TABLE PackageComments ADD COLUMN RenderedComment TEXT NOT NULL;
 ---
+
+3. Add Terms and AcceptedTerms tables:
+
+---
+CREATE TABLE Terms (
+       ID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+       Description VARCHAR(255) NOT NULL,
+       URL VARCHAR(8000) NOT NULL,
+       Revision INTEGER UNSIGNED NOT NULL DEFAULT 1,
+       PRIMARY KEY (ID)
+) ENGINE = InnoDB;
+
+CREATE TABLE AcceptedTerms (
+       UsersID INTEGER UNSIGNED NOT NULL,
+       TermsID INTEGER UNSIGNED NOT NULL,
+       Revision INTEGER UNSIGNED NOT NULL DEFAULT 0,
+       FOREIGN KEY (UsersID) REFERENCES Users(ID) ON DELETE CASCADE,
+       FOREIGN KEY (TermsID) REFERENCES Terms(ID) ON DELETE CASCADE
+) ENGINE = InnoDB;
+---
diff --git a/web/html/tos.php b/web/html/tos.php
new file mode 100644
index 0000000..135fa1e
--- /dev/null
+++ b/web/html/tos.php
@@ -0,0 +1,50 @@
+<?php
+set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
+
+include_once("aur.inc.php");
+
+$uid = uid_from_sid($_COOKIE["AURSID"]);
+
+if (isset($_POST["accept"]) && $_POST["accept"]) {
+       accept_terms($uid, $_POST["rev"]);
+       header("Location: " . get_uri('/'));
+}
+
+$terms = fetch_updated_terms($uid);
+if (!$terms) {
+       header("Location: " . get_uri('/'));
+}
+
+html_header('AUR ' . __("Terms of Service"));
+?>
+<div id="dev-login" class="box">
+       <h2>AUR <?= __('Terms of Service') ?></h2>
+       <?php if (isset($_COOKIE["AURSID"])): ?>
+       <form method="post" action="<?= get_uri('/tos') ?>">
+               <fieldset>
+                       <p>
+                               <?= __("Logged-in as: %s", '<strong>' . 
username_from_sid($_COOKIE["AURSID"]) . '</strong>'); ?>
+                       </p>
+                       <p>
+                               <?= __("The following documents have been 
updated. Please review them carefully:"); ?>
+                       </p>
+                       <ul>
+                       <?php foreach($terms as $row): ?>
+                               <li><a href="<?= urlencode(sprintf($row["URL"], 
$row["Revision"])) ?>"><?= htmlspecialchars($row["Description"]) ?></a> (<?= 
__('revision %d', $row["Revision"]) ?>)</li>
+                       <?php endforeach; ?>
+                       </ul>
+                       <p>
+                               <?php foreach($terms as $row): ?>
+                                       <input type="hidden" name="rev[<?= 
$row["ID"] ?>]" value="<?= $row["Revision"] ?>" />
+                               <?php endforeach; ?>
+                               <input type="checkbox" name="accept" /> <?= 
__("I accept the terms and conditions above."); ?>
+                       </p>
+                       <p>
+                               <input type="submit" name="submit" value="<?= 
__("Continue") ?>" />
+                       </p>
+               </fieldset>
+       </form>
+       <?php endif; ?>
+</div>
+<?php
+html_footer(AURWEB_VERSION);
diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php
index 22b3ca8..e45d735 100644
--- a/web/lib/acctfuncs.inc.php
+++ b/web/lib/acctfuncs.inc.php
@@ -1325,3 +1325,77 @@ function notify($params) {
 
        return proc_close($p);
 }
+
+/*
+ * Obtain a list of terms a given user has not yet accepted.
+ *
+ * @param int $uid The ID of the user to obtain terms for.
+ *
+ * @return array A list of terms the user has not yet accepted.
+ */
+function fetch_updated_terms($uid) {
+       $dbh = DB::connect();
+
+       $q = "SELECT ID, Terms.Revision, Description, URL ";
+       $q .= "FROM Terms LEFT JOIN AcceptedTerms ";
+       $q .= "ON AcceptedTerms.TermsID = Terms.ID ";
+       $q .= "AND AcceptedTerms.UsersID = " . intval($uid) . " ";
+       $q .= "WHERE AcceptedTerms.Revision IS NULL OR ";
+       $q .= "AcceptedTerms.Revision < Terms.Revision";
+
+       $result = $dbh->query($q);
+
+       if ($result) {
+               return $result->fetchAll();
+       } else {
+               return array();
+       }
+}
+
+/*
+ * Accept a list of given terms.
+ *
+ * @param int $uid The ID of the user to accept the terms.
+ * @param array $termrev An array mapping each term to the accepted revision.
+ *
+ * @return void
+ */
+function accept_terms($uid, $termrev) {
+       $dbh = DB::connect();
+
+       $q = "SELECT TermsID, Revision FROM AcceptedTerms ";
+       $q .= "WHERE UsersID = " . intval($uid);
+
+       $result = $dbh->query($q);
+
+       if (!$result) {
+               return;
+       }
+
+       $termrev_update = array();
+       while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+               $id = $row['TermsID'];
+               if (!array_key_exists($id, $termrev)) {
+                       continue;
+               }
+               if ($row['Revision'] < $termrev[$id]) {
+                       $termrev_update[$id] = $termrev[$id];
+               }
+       }
+       $termrev_add = array_diff_key($termrev, $termrev_update);
+
+       foreach ($termrev_add as $id => $rev) {
+               $q = "INSERT INTO AcceptedTerms (TermsID, UsersID, Revision) ";
+               $q .= "VALUES (" . intval($id) . ", " . intval($uid) . ", ";
+               $q .= intval($rev) . ")";
+               $dbh->exec($q);
+       }
+
+       foreach ($termrev_update as $id => $rev) {
+               $q = "UPDATE AcceptedTerms ";
+               $q .= "SET Revision = " . intval($rev) . " ";
+               $q .= "WHERE TermsID = " . intval($id) . " AND ";
+               $q .= "UsersID = " . intval($uid);
+               $dbh->exec($q);
+       }
+}
diff --git a/web/lib/aur.inc.php b/web/lib/aur.inc.php
index 063de8f..ce569ea 100644
--- a/web/lib/aur.inc.php
+++ b/web/lib/aur.inc.php
@@ -22,6 +22,7 @@ include_once('timezone.inc.php');
 set_tz();
 
 check_sid();
+check_tos();
 
 /**
  * Check if a visitor is logged in
@@ -92,6 +93,28 @@ function check_sid() {
 }
 
 /**
+ * Redirect user to the Terms of Service agreement if there are updated terms.
+ *
+ * @return void
+ */
+function check_tos() {
+       if (!isset($_COOKIE["AURSID"])) {
+               return;
+       }
+
+       $path = $_SERVER['PATH_INFO'];
+       $route = get_route($path);
+       if (!$route || $route == "tos.php") {
+               return;
+       }
+
+       if (count(fetch_updated_terms(uid_from_sid($_COOKIE["AURSID"]))) > 0) {
+               header('Location: ' . get_uri('/tos'));
+               exit();
+       }
+}
+
+/**
  * Verify the supplied CSRF token matches expected token
  *
  * @return bool True if the CSRF token is the same as the cookie SID, 
otherwise false
diff --git a/web/lib/routing.inc.php b/web/lib/routing.inc.php
index 8c45c62..7d9750a 100644
--- a/web/lib/routing.inc.php
+++ b/web/lib/routing.inc.php
@@ -16,6 +16,7 @@ $ROUTES = array(
        '/passreset' => 'passreset.php',
        '/rpc' => 'rpc.php',
        '/rss' => 'rss.php',
+       '/tos' => 'tos.php',
        '/tu' => 'tu.php',
        '/addvote' => 'addvote.php',
 );
-- 
2.12.2

Reply via email to