[aur-dev] [PATCH 1/6] Add support for filing package requests
Add a new entry to the package actions box that allows for filing
deletion and orphan requests. When choosing that action, the user is
redirected to a new page that allows for selecting a request type and
entering a comment. When submitting the request, a new entry in the
request database is created and an email is sent to a configurable
mailing list (defaults to aur-general).
Signed-off-by: Lukas Fleischer
Introduce a new navigation point "Requests" that shows a list of pending
package requests. This functionality is only available to Trusted Users.
Signed-off-by: Lukas Fleischer
This allows Trusted Users to close package requests via the request
list. Also, entries are now sorted such that open requests are shown
before closed requests.
Signed-off-by: Lukas Fleischer
Automatically highlight package requests after a configurable period of
time. Defaults to 14 days.
Signed-off-by: Lukas Fleischer This button allows for accepting a request, disowning the affected
package or redirecting to the package deletion page. The request is
closed automatically when the action has been performed.
Signed-off-by: Lukas Fleischer This adds a new "Merge" category to the list of available request types
and also adds a new "Merge into" field that is hidden via JavaScript
when "Deletion" or "Orphan" is selected.
Signed-off-by: Lukas Fleischer On 25.06.2014 11:44, Lukas Fleischer wrote: Introduce a new navigation point "Requests" that shows a list of pending
package requests. This functionality is only available to Trusted Users. Signed-off-by: Lukas Fleischer diff --git a/web/html/pkgreq.php b/web/html/pkgreq.php
index c8dd673..05eeb51 100644
--- a/web/html/pkgreq.php
+++ b/web/html/pkgreq.php
@@ -14,6 +14,62 @@ if (!check_user_privileges()) {
header('Location: /');
exit();
}
+
+if (!isset($base_id)) {
+ $results = pkgbase_request_list();
+ $total = count($results);
+
+ /* Sanitize paging variables. */
+ if (isset($_GET['O'])) { I understand this is copied from pkg_search_page() in
web/lib/pkgfuncs.inc.php, but now would be a good time to clean it up
(in both places). Especially the usage of $_GET rather than a local
variable should go away.
Introduce something like input_get($field_name, $default = false); which
would check if the value is set and if it is return it, otherwise return
the default. + $_GET['O'] = intval($_GET['O']);
+ if ($_GET['O'] < 0)
+ $_GET['O'] = 0; $value = max(intval($value), 0); + } else {
+ $_GET['O'] = 0;
+ }
+
+
+ if (isset($_GET["PP"])) {
+ $_GET["PP"] = intval($_GET["PP"]);
+ if ($_GET["PP"] < 50)
+ $_GET["PP"] = 50;
+ else if ($_GET["PP"] > 250)
+ $_GET["PP"] = 250; # Source: http://at2.php.net/manual/en/function.min.php#81302
function bound($x, $min, $max)
{
return min(max($x, $min), $max);
} + } else {
+ $_GET["PP"] = 50;
+ }
+
+ /* Calculate the results to use. */
+ $first = $_GET['O'] + 1;
+
+ /* Calculation of pagination links. */
+ $per_page = ($_GET['PP'] > 0) ? $_GET['PP'] : 50;
+ $current = ceil($first / $per_page);
+ $pages = ceil($total / $per_page);
+ $templ_pages = array();
+
+ if ($current > 1) {
+ $templ_pages['« ' . __('First')] = 0;
+ $templ_pages['‹ ' . __('Previous')] = ($current - 2) * $per_page;
+ }
+
+ if ($current - 5 > 1)
+ $templ_pages["..."] = false;
+
+ for ($i = max($current - 5, 1); $i <= min($pages, $current + 5); $i++) {
+ $templ_pages[$i] = ($i - 1) * $per_page;
+ }
+
+ if ($current + 5 < $pages)
+ $templ_pages["... "] = false;
+
+ if ($current < $pages) {
+ $templ_pages[__('Next') . ' ›'] = $current * $per_page;
+ $templ_pages[__('Last') . ' »'] = ($pages - 1) * $per_page;
+ }
+
+ $SID = $_COOKIE['AURSID'];
+ include('pkgreq_results.php');
+} else {
?> <div class="box">
@@ -51,5 +107,7 @@ if (!check_user_privileges()) {
</div>
diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php
index a9fe949..505bb51 100644
--- a/web/lib/pkgbasefuncs.inc.php
+++ b/web/lib/pkgbasefuncs.inc.php
@@ -964,6 +964,26 @@ function pkgbase_update_category($base_id, $category_id) {
} /**
+ * Get a list of all package requests
+ *
+ * @return array List of pacakge requests with details
+ */
+function pkgbase_request_list() {
+ $dbh = DB::connect();
+
+ $q = "SELECT PackageRequests.ID, ";
+ $q.= "PackageRequests.PackageBaseID AS BaseID, ";
+ $q.= "PackageRequests.PackageBaseName AS Name, ";
+ $q.= "RequestTypes.Name AS Type, PackageRequests.Comments, ";
+ $q.= "Users.Username AS User, PackageRequests.RequestTS ";
+ $q.= "FROM PackageRequests INNER JOIN RequestTypes ON ";
+ $q.= "RequestTypes.ID = PackageRequests.ReqTypeID ";
+ $q.= "INNER JOIN Users ON Users.ID = PackageRequests.UsersID";
+
+ return $dbh->query($q)->fetchAll();
+}
+
+/**
* File a deletion/orphan request against a package base
*
* @global string $AUR_LOCATION The AUR's URL used for notification e-mails
diff --git a/web/lib/routing.inc.php b/web/lib/routing.inc.php
index 1b2aa52..5836a13 100644
--- a/web/lib/routing.inc.php
+++ b/web/lib/routing.inc.php
@@ -5,6 +5,7 @@ $ROUTES = array(
'/index.php' => 'home.php',
'/packages' => 'packages.php',
'/pkgbase' => 'pkgbase.php',
+ '/requests' => 'pkgreq.php',
'/register' => 'account.php',
'/account' => 'account.php',
'/accounts' => 'account.php',
diff --git a/web/template/header.php b/web/template/header.php
index df83995..03ce536 100644
--- a/web/template/header.php
+++ b/web/template/header.php
@@ -57,6 +57,9 @@
<li><a href="<?= get_uri('/packages/'); ?>"><?= __("Packages"); ?></a></li>
<?php if (isset($_COOKIE['AURSID'])): ?>
<li><?= __("My Packages"); ?></a></li>
+ <?php if (check_user_privileges()): ?>
+ <li><a href="<?= get_uri('/requests/') ; ?>"><?= __("Requests"); ?></a></li>
+ <?php endif; ?>
<li><a href="<?= get_uri('/submit/'); ?>"><?= __("Submit"); ?></a></li>
<?php if (check_user_privileges()): ?>
<li><a href="<?= get_uri('/accounts/') ; ?>"><?= __("Accounts"); ?></a></li>
diff --git a/web/template/pkgreq_results.php b/web/template/pkgreq_results.php
new file mode 100644
index 0000000..7cbdcc4
--- /dev/null
+++ b/web/template/pkgreq_results.php
@@ -0,0 +1,66 @@
+<div id="pkglist-results" class="box">
+ <div class="pkglist-stats">
+ <p><?= __('%d package requests found. Page %d of %d.', $total, $current, $pages) ?></p>
+ <?php if (count($templ_pages) > 1): ?>
+ <p class="pkglist-nav">
+ <?php foreach ($templ_pages as $pagenr => $pagestart): ?>
+ <?php if ($pagestart === false): ?>
+ <span class="page"><?= $pagenr ?></span>
+ <?php elseif ($pagestart + 1 == $first): ?>
+ <span class="page"><?= $pagenr ?></span>
+ <?php else: ?>
+ <a class="page" href="<?= get_uri('/requests/'); ?>?<?= mkurl('O=' . $pagestart) ?>"><?= $pagenr ?></a>
+ <?php endif; ?>
+ <?php endforeach; ?>
+ </p>
+ <?php endif; ?>
+ </div>
+
+ <table class="results">
+ <thead>
+ <tr>
+ <th><?= __("Package") ?></th>
+ <th><?= __("Type") ?></th>
+ <th><?= __("Comments") ?></th>
+ <th><?= __("Filed by") ?></th>
+ <th><?= __("Date") ?></th>
+ </tr>
+ </thead>
+ <tbody>
+
+ <?php while (list($indx, $row) = each($results)): ?>
+ <tr class="<?= ($indx % 2 == 0) ? 'odd' : 'even' ?>">
+ <?php if ($row['BaseID']): ?>
+ <td><?= htmlspecialchars($row["Name"]) ?></a></td>
+ <?php else: ?>
+ <td><?= htmlspecialchars($row["Name"]) ?></td>
+ <?php endif; ?>
+ <td><?= htmlspecialchars(ucfirst($row['Type']), ENT_QUOTES); ?></td>
+ <td class="wrap"><?= htmlspecialchars($row['Comments'], ENT_QUOTES); ?></td>
+ <td>
+ <a href="<?= get_uri('/account/') . htmlspecialchars($row['User'], ENT_QUOTES) ?>" title="<?= __('View account information for %s', htmlspecialchars($row['User'])) ?>"><?= htmlspecialchars($row['User']) ?></a>
+ </td>
+ <td><?= gmdate("Y-m-d H:i", intval($row['RequestTS'])) ?></td>
+ </tr>
+ <?php endwhile; ?>
+
+ </tbody>
+ </table>
+
+ <div class="pkglist-stats">
+ <p><?= __('%d package requests found. Page %d of %d.', $total, $current, $pages) ?></p>
+ <?php if (count($templ_pages) > 1): ?>
+ <p class="pkglist-nav">
+ <?php foreach ($templ_pages as $pagenr => $pagestart): ?>
+ <?php if ($pagestart === false): ?>
+ <span class="page"><?= $pagenr ?></span>
+ <?php elseif ($pagestart + 1 == $first): ?>
+ <span class="page"><?= $pagenr ?></span>
+ <?php else: ?>
+ <a class="page" href="<?= get_uri('/requests/'); ?>?<?= mkurl('O=' . $pagestart) ?>"><?= $pagenr ?></a>
+ <?php endif; ?>
+ <?php endforeach; ?>
+ </p>
+ <?php endif; ?>
+ </div>
+</div> On 25.06.2014 11:44, Lukas Fleischer wrote: This allows Trusted Users to close package requests via the request
list. Also, entries are now sorted such that open requests are shown
before closed requests. Signed-off-by: Lukas Fleischer [...] diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php
index 505bb51..a039f83 100644
--- a/web/lib/pkgbasefuncs.inc.php
+++ b/web/lib/pkgbasefuncs.inc.php
@@ -1070,3 +1072,23 @@ function pkgbase_file_request($ids, $type, $comments) { return array(true, __("Added request successfully."));
}
+
+/**
+ * Close a deletion/orphan request
+ *
+ * @param int $id The package request to close
+ *
+ * @return void You return an array, not void. + */
+function pkgbase_close_request($id) {
+ $dbh = DB::connect();
+
+ if (!check_user_privileges()) {
+ return array(false, __("Only TUs and developers can close requests."));
+ }
+
+ $q = "UPDATE PackageRequests SET Status = 1 WHERE ID = " . intval($id);
+ $dbh->exec($q);
+
+ return array(true, __("Request closed successfully."));
+} On 25.06.2014 11:44, Lukas Fleischer wrote: Add a new entry to the package actions box that allows for filing
deletion and orphan requests. When choosing that action, the user is
redirected to a new page that allows for selecting a request type and
entering a comment. When submitting the request, a new entry in the
request database is created and an email is sent to a configurable
mailing list (defaults to aur-general). Signed-off-by: Lukas Fleischer [..] diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php
index 9f80ef2..a9fe949 100644
--- a/web/lib/pkgbasefuncs.inc.php
+++ b/web/lib/pkgbasefuncs.inc.php
@@ -962,3 +962,91 @@ function pkgbase_update_category($base_id, $category_id) {
$category_id, $base_id);
$dbh->exec($q);
}
+
+/**
+ * File a deletion/orphan request against a package base
+ *
+ * @global string $AUR_LOCATION The AUR's URL used for notification e-mails
+ * @global string $AUR_REQUEST_ML The request notification mailing list
+ * @param string $ids The package base IDs to file the request against
+ * @param string $type The type of the request
+ * @param string $comments The comments to be added to the request
+ *
+ * @return void
+ */
+function pkgbase_file_request($ids, $type, $comments) {
+ global $AUR_LOCATION;
+ global $AUR_REQUEST_ML;
+
+ if (empty($comments)) {
+ return array(false, __("The comment field must not be empty."));
+ }
+
+ $dbh = DB::connect();
+ $uid = uid_from_sid($_COOKIE["AURSID"]);
+
+ /* TODO: Allow for filing multiple requests at once. */
+ $base_id = $ids[0];
+ $pkgbase_name = pkgbase_name_from_id($base_id);
+
+ $q = "SELECT ID FROM RequestTypes WHERE Name = " . $dbh->quote($type);
+ $result = $dbh->query($q);
+ if ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+ $type_id = $row['ID'];
+ } else {
+ return array(false, __("Invalid request type."));
+ }
+
+ $q = "INSERT INTO PackageRequests ";
+ $q.= "(ReqTypeID, PackageBaseID, PackageBaseName, UsersID, ";
+ $q.= "Comments, RequestTS) VALUES (" . $type_id . ", ";
+ $q.= intval($base_id) . ", " . $dbh->quote($pkgbase_name) . ", ";
+ $q.= $uid . ", " . $dbh->quote($comments) . ", UNIX_TIMESTAMP())";
+ $dbh->exec($q);
+
+ /*
+ * Send e-mail notifications.
+ * TODO: Move notification logic to separate function where it belongs.
+ */
+ $q = "SELECT Users.Email ";
+ $q.= "FROM Users INNER JOIN PackageBases ";
+ $q.= "ON PackageBases.MaintainerUID = Users.ID ";
+ $q.= "WHERE PackageBases.ID = " . intval($base_id);
+ $result = $dbh->query($q);
+ if ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+ $bcc = $row['Email'];
+ } else {
+ unset($bcc);
+ }
+
+ $q = "SELECT Name FROM PackageBases WHERE ID = ";
+ $q.= intval($base_id);
+ $result = $dbh->query($q);
+ $row = $result->fetch(PDO::FETCH_ASSOC);
+
+ /*
+ * TODO: Add native language emails for users, based on their
+ * preferences. Simply making these strings translatable won't
+ * work, users would be getting emails in the language that the
+ * user who posted the comment was in.
+ */
+ $username = username_from_sid($_COOKIE['AURSID']);
+ $body =
+ $username . " [1] filed a " . $type . " request for " .
+ $row['Name'] . " [2]:\n\n" . $comments . "\n\n" .
+ "[1] " . $AUR_LOCATION . get_user_uri($username) . "\n" .
+ "[2] " . $AUR_LOCATION . get_pkgbase_uri($row['Name']) . "\n";
+ $body = wordwrap($body, 70);
+ $headers = "MIME-Version: 1.0\r\n" .
+ "Content-type: text/plain; charset=UTF-8\r\n";
+ if (!empty($bcc)) {
+ $headers .= "Bcc: $bcc\r\n";
+ }
+ $headers .= "Reply-to: noreply@aur.archlinux.org\r\n" .
+ "From: notify@aur.archlinux.org\r\n" .
+ "X-Mailer: AUR"; Can you set a References header here so accept requests generated via
the webui will be able to thread properly? Something like "References:
+ @mail($AUR_REQUEST_ML, "AUR " . ucfirst($type) . " Request for " .
+ $row['Name'], $body, $headers);
+
+ return array(true, __("Added request successfully."));
+} On Tue, 01 Jul 2014 at 18:37:46, Florian Pritz wrote: [...]
I understand this is copied from pkg_search_page() in
web/lib/pkgfuncs.inc.php, but now would be a good time to clean it up
(in both places). Especially the usage of $_GET rather than a local
variable should go away. Introduce something like input_get($field_name, $default = false); which
would check if the value is set and if it is return it, otherwise return
the default. I prepared several patches to improve GET parameter handling and
permission handling but I am not going to merge them before the 3.2.0
release. + $_GET['O'] = intval($_GET['O']);
+ if ($_GET['O'] < 0)
+ $_GET['O'] = 0; $value = max(intval($value), 0); + } else {
+ $_GET['O'] = 0;
+ }
+
+
+ if (isset($_GET["PP"])) {
+ $_GET["PP"] = intval($_GET["PP"]);
+ if ($_GET["PP"] < 50)
+ $_GET["PP"] = 50;
+ else if ($_GET["PP"] > 250)
+ $_GET["PP"] = 250; # Source: http://at2.php.net/manual/en/function.min.php#81302
function bound($x, $min, $max)
{
return min(max($x, $min), $max);
}
[...] Thanks, I will send a patch to simplify these two checks (the patch
series has already been merged into master, so it is too late to amend). On Tue, 01 Jul 2014 at 18:42:09, Florian Pritz wrote: [...] +
+/**
+ * Close a deletion/orphan request
+ *
+ * @param int $id The package request to close
+ *
+ * @return void You return an array, not void. Will send a patch to fix the documentation, thanks. + */
+function pkgbase_close_request($id) {
+ $dbh = DB::connect();
+
+ if (!check_user_privileges()) {
+ return array(false, __("Only TUs and developers can close requests."));
+ }
+
+ $q = "UPDATE PackageRequests SET Status = 1 WHERE ID = " . intval($id);
+ $dbh->exec($q);
+
+ return array(true, __("Request closed successfully."));
+} On Tue, 01 Jul 2014 at 18:50:33, Florian Pritz wrote: [...] + /*
+ * TODO: Add native language emails for users, based on their
+ * preferences. Simply making these strings translatable won't
+ * work, users would be getting emails in the language that the
+ * user who posted the comment was in.
+ */
+ $username = username_from_sid($_COOKIE['AURSID']);
+ $body =
+ $username . " [1] filed a " . $type . " request for " .
+ $row['Name'] . " [2]:\n\n" . $comments . "\n\n" .
+ "[1] " . $AUR_LOCATION . get_user_uri($username) . "\n" .
+ "[2] " . $AUR_LOCATION . get_pkgbase_uri($row['Name']) . "\n";
+ $body = wordwrap($body, 70);
+ $headers = "MIME-Version: 1.0\r\n" .
+ "Content-type: text/plain; charset=UTF-8\r\n";
+ if (!empty($bcc)) {
+ $headers .= "Bcc: $bcc\r\n";
+ }
+ $headers .= "Reply-to: noreply@aur.archlinux.org\r\n" .
+ "From: notify@aur.archlinux.org\r\n" .
+ "X-Mailer: AUR"; Can you set a References header here so accept requests generated via
the webui will be able to thread properly? Something like "References:
See commit 0e84667 (Add threading headers to request notification mails,
2014-06-25) in master. + @mail($AUR_REQUEST_ML, "AUR " . ucfirst($type) . " Request for " .
+ $row['Name'], $body, $headers);
+
+ return array(true, __("Added request successfully."));
+}
<?= gmdate("Y-m-d H:i", intval($row['RequestTS'])) ?></td>
<?php if ($row['Status'] == 0): ?>
<td>
<form action="<?= get_uri('/pkgbase/'); ?>" method="post">
--
2.0.0
<?= gmdate("Y-m-d H:i", intval($row['RequestTS'])) ?></td>
<?php if ($row['Status'] == 0): ?>
<td>
+ <?php if ($row['BaseID']): ?>
+ <?php if ($row['Type'] == 'deletion'): ?>
+ <a href="<?= get_pkgbase_uri($row['Name']) ?>delete/?via=<?= intval($row['ID']) ?>"><?= __('Accept') ?></a>
+ <?php elseif ($row['Type'] == 'orphan'): ?>
+ <form action="<?= get_pkgbase_uri($row['Name']) . 'disown/'; ?>" method="post">
+ <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" />
+ <input type="hidden" name="via" value="<?= intval($row['ID']) ?>" />
+ <input type="submit" class="button text-button" name="do_Disown" value="<?= __('Accept') ?>" />
+ </form>
+ <?php endif; ?>
+ <?php endif; ?>
<form action="<?= get_uri('/pkgbase/'); ?>" method="post">
<fieldset>
<input type="hidden" name="IDs[<?= $row['BaseID'] ?>]" value="1" />
--
2.0.0
participants (2)