[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
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
--- web/html/pkgreq.php | 58 ++++++++++++++++++++++++++++++++++++ web/lib/pkgbasefuncs.inc.php | 20 +++++++++++++ web/lib/routing.inc.php | 1 + web/template/header.php | 3 ++ web/template/pkgreq_results.php | 66 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 web/template/pkgreq_results.php 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 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).
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
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
--- UPGRADING | 1 + schema/aur-schema.sql | 1 + web/html/css/aur.css | 30 ++++++++++++++++-------------- web/html/pkgbase.php | 8 +++++++- web/lib/pkgbasefuncs.inc.php | 26 ++++++++++++++++++++++++-- web/lib/routing.inc.php | 6 ++++++ web/template/pkgreq_results.php | 18 ++++++++++++++++++ 7 files changed, 73 insertions(+), 17 deletions(-)
[...]
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 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.")); +}
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: 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: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)