[aur-dev] [PATCH v2] Implement capability to pin comments above others
Adds functions and credential information to pin comments before others. This needs two extra columns (PinnedTS and PinnedUsersID) to the PackageComments table. Implements FS#10863 Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- web/html/css/aurweb.css | 18 ++++++++-- web/html/pkgbase.php | 4 +++ web/lib/credentials.inc.php | 2 ++ web/lib/pkgbasefuncs.inc.php | 82 +++++++++++++++++++++++++++++++++++++++++-- web/lib/pkgfuncs.inc.php | 51 ++++++++++++++++++++++++++- web/template/pkg_comments.php | 41 +++++++++++++++++++--- 6 files changed, 187 insertions(+), 11 deletions(-) diff --git a/web/html/css/aurweb.css b/web/html/css/aurweb.css index 11af747..5b36df0 100644 --- a/web/html/css/aurweb.css +++ b/web/html/css/aurweb.css @@ -101,7 +101,7 @@ color: #999; } -.delete-comment-form, .edit-comment { +.delete-comment-form, .pin-comment-form, .edit-comment { float: right; margin-left: 8px; } @@ -118,12 +118,26 @@ opacity: 0.6; } -.delete-comment:hover, .edit-comment:hover { +.delete-comment:hover, .edit-comment:hover, .pin-comment:hover { -webkit-filter: none; filter: none; opacity: 1; } +.pin-comment { + float: right; + font-weight: 600; + background: none!important; + border: none; + padding: 0!important; + font: inherit; + cursor: pointer; + color: #3366aa; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); + opacity: 0.6; +} + .ajax-loader { float: right; position: relative; diff --git a/web/html/pkgbase.php b/web/html/pkgbase.php index cbbf3cc..6f0de08 100644 --- a/web/html/pkgbase.php +++ b/web/html/pkgbase.php @@ -99,6 +99,10 @@ if (check_token()) { list($ret, $output) = pkgbase_notify($ids, false); } elseif (current_action("do_DeleteComment")) { list($ret, $output) = pkgbase_delete_comment(); + } elseif (current_action("do_PinComment")) { + list($ret, $output) = pkgbase_pin_comment(); + } elseif (current_action("do_UnpinComment")) { + list($ret, $output) = pkgbase_unpin_comment(); } elseif (current_action("do_SetKeywords")) { list($ret, $output) = pkgbase_set_keywords($base_id, preg_split("/[\s,;]+/", $_POST['keywords'], -1, PREG_SPLIT_NO_EMPTY)); } elseif (current_action("do_FileRequest")) { diff --git a/web/lib/credentials.inc.php b/web/lib/credentials.inc.php index 648d78c..71bf5ff 100644 --- a/web/lib/credentials.inc.php +++ b/web/lib/credentials.inc.php @@ -8,6 +8,7 @@ define("CRED_ACCOUNT_SEARCH", 5); define("CRED_COMMENT_DELETE", 6); define("CRED_COMMENT_VIEW_DELETED", 22); define("CRED_COMMENT_EDIT", 25); +define("CRED_COMMENT_PIN", 26); define("CRED_PKGBASE_ADOPT", 7); define("CRED_PKGBASE_SET_KEYWORDS", 8); define("CRED_PKGBASE_DELETE", 9); @@ -60,6 +61,7 @@ function has_credential($credential, $approved_users=array()) { case CRED_COMMENT_DELETE: case CRED_COMMENT_VIEW_DELETED: case CRED_COMMENT_EDIT: + case CRED_COMMENT_PIN: case CRED_PKGBASE_ADOPT: case CRED_PKGBASE_SET_KEYWORDS: case CRED_PKGBASE_DELETE: diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php index afccc7d..ea3ce0d 100644 --- a/web/lib/pkgbasefuncs.inc.php +++ b/web/lib/pkgbasefuncs.inc.php @@ -36,10 +36,11 @@ function pkgbase_comments_count($base_id, $include_deleted) { * @param int $base_id The package base ID to get comments for * @param int $limit Maximum number of comments to return (0 means unlimited) * @param bool $include_deleted True if deleted comments should be included + * @param bool $show_only_pinned True when only pinned comments are to be included * * @return array All package comment information for a specific package base */ -function pkgbase_comments($base_id, $limit, $include_deleted) { +function pkgbase_comments($base_id, $limit, $include_deleted, $show_only_pinned=false) { $base_id = intval($base_id); $limit = intval($limit); if (!$base_id) { @@ -49,11 +50,17 @@ function pkgbase_comments($base_id, $limit, $include_deleted) { $dbh = DB::connect(); $q = "SELECT PackageComments.ID, A.UserName AS UserName, UsersID, Comments, "; $q.= "CommentTS, EditedTS, B.UserName AS EditUserName, "; - $q.= "DelUsersID, C.UserName AS DelUserName FROM PackageComments "; + $q.= "DelUsersID, C.UserName AS DelUserName, "; + $q.= "PinnedUsersID, D.UserName AS PinnedUserName FROM PackageComments "; $q.= "LEFT JOIN Users A ON PackageComments.UsersID = A.ID "; $q.= "LEFT JOIN Users B ON PackageComments.EditedUsersID = B.ID "; $q.= "LEFT JOIN Users C ON PackageComments.DelUsersID = C.ID "; + $q.= "LEFT JOIN Users D ON PackageComments.PinnedUsersID = D.ID "; $q.= "WHERE PackageBaseID = " . $base_id . " "; + + if ($show_only_pinned) { + $q.= "AND PinnedUsersID IS NOT NULL "; + } if (!$include_deleted) { $q.= "AND DelUsersID IS NULL "; } @@ -111,6 +118,67 @@ function pkgbase_add_comment($base_id, $uid, $comment) { } /** + * Pin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_pin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + $dbh = DB::connect(); + if (can_pin_comment($comment_id)) { + $q = "UPDATE PackageComments "; + $q.= "SET PinnedUsersID = ".$uid.", "; + $q.= "PinnedTS = UNIX_TIMESTAMP() "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been pinned.")); + } else { + return array(false, __("You are not allowed to pin this comment.")); + } +} + +/** + * Unpin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_unpin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + $dbh = DB::connect(); + if (can_pin_comment($comment_id)) { + $q = "UPDATE PackageComments "; + $q.= "SET PinnedUsersID = NULL, "; + $q.= "PinnedTS = UNIX_TIMESTAMP() "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been unpinned.")); + } else { + return array(false, __("You are not allowed to unpin this comment.")); + } +} + +/** + * Get a list of all packages a logged-in user has voted for * * @param string $sid The session ID of the visitor @@ -196,9 +264,17 @@ function pkgbase_display_details($base_id, $row, $SID="") { if ($SID) { include('pkg_comment_box.php'); } + + $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } $limit = isset($_GET['comments']) ? 0 : 10; - $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/lib/pkgfuncs.inc.php b/web/lib/pkgfuncs.inc.php index cedc360..c0ed99c 100644 --- a/web/lib/pkgfuncs.inc.php +++ b/web/lib/pkgfuncs.inc.php @@ -83,6 +83,47 @@ function can_edit_comment_array($comment) { } /** + * Determine if the user can pin a specific package comment + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the backend side of comment pinning. + * + * @param string $comment_id The comment ID in the database + * + * @return bool True if the user can pin the comment, otherwise false + */ +function can_pin_comment($comment_id=0) { + $dbh = DB::connect(); + + $q = "SELECT MaintainerUID FROM PackageBases AS pb "; + $q.= "LEFT JOIN PackageComments AS pc ON pb.ID = pc.PackageBaseID "; + $q.= "WHERE pc.ID = " . intval($comment_id); + $result = $dbh->query($q); + + if (!$result) { + return false; + } + + $uid = $result->fetch(PDO::FETCH_COLUMN, 0); + + return has_credential(CRED_COMMENT_PIN, array($uid)); +} + +/** + * Determine if the user can edit a specific package comment using an array + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the frontend side of comment pinning. + * + * @param array $comment All database information relating a specific comment + * + * @return bool True if the user can edit the comment, otherwise false + */ +function can_pin_comment_array($comment) { + return can_pin_comment($comment['ID']); +} + +/** * Check to see if the package name already exists in the database * * @param string $name The package name to check @@ -581,9 +622,17 @@ function pkg_display_details($id=0, $row, $SID="") { if ($SID) { include('pkg_comment_box.php'); } + + $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } $limit = isset($_GET['comments']) ? 0 : 10; - $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/template/pkg_comments.php b/web/template/pkg_comments.php index 21ce16f..8f1fb9f 100644 --- a/web/template/pkg_comments.php +++ b/web/template/pkg_comments.php @@ -6,13 +6,20 @@ if (isset($row['BaseID'])) { /* On a package base details page. */ $base_id = $row['ID']; } -$include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); -$count = pkgbase_comments_count($base_id, $include_deleted); +if (!isset($count)) { + $count = pkgbase_comments_count($base_id, $include_deleted); +} ?> <div id="news"> <h3> - <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> - <span class="arrow"></span> + <?php if (!isset($comments)): ?> + <?php $comments = $pinned ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Pinned Comments') ?></a> + <span class="arrow"></span> + <?php else: ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> + <span class="arrow"></span> + <?php endif; ?> </h3> <?php while (list($indx, $row) = each($comments)): ?> @@ -56,6 +63,30 @@ $count = pkgbase_comments_count($base_id, $include_deleted); <?php if (!$row['DelUsersID'] && can_edit_comment_array($row)): ?> <a href="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name) . 'edit-comment/?comment_id=' . $row['ID'], ENT_QUOTES) ?>" class="edit-comment" title="<?= __('Edit comment') ?>"><img src="/images/pencil.min.svg" alt="<?= __('Edit comment') ?>" width="11" height="11"></a> <?php endif; ?> + + <?php if (!$row['DelUsersID'] && !$row['PinnedUsersID'] && can_pin_comment_array($row)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_PinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="submit" class="pin-comment" value="<?= __('Pin') ?>" width="11" height="11" + alt="<?= __('Pin comment') ?>" title="<?= __('Pin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> + + <?php if (!$row['DelUsersID'] && $row['PinnedUsersID'] && can_pin_comment_array($row)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_UnpinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="submit" class="pin-comment" value="<?= __('Unpin') ?>" width="11" height="11" + alt="<?= __('Unin comment') ?>" title="<?= __('Pin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> </h4> <div class="article-content<?php if ($row['DelUsersID']): ?> comment-deleted<?php endif; ?>"> <p> @@ -64,7 +95,7 @@ $count = pkgbase_comments_count($base_id, $include_deleted); </div> <?php endwhile; ?> -<?php if ($count > 10 && !isset($_GET['comments'])): ?> +<?php if ($count > 10 && !isset($_GET['comments']) && !isset($pinned)): ?> <h3> <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments') ?> (<?= $count ?>)"><?= __('All comments', $count) ?></a> </h3> -- 2.6.3
On 03/12, Mark Weiman wrote:
Adds functions and credential information to pin comments before others.
This needs two extra columns (PinnedTS and PinnedUsersID) to the PackageComments table.
Implements FS#10863
Signed-off-by: Mark Weiman <mark.weiman@markzz.com> ---
Two quick notes: 1) You sent something you called v2 twice, which is a bit confusing. 2) It would be useful if you put a comment on what changed from the last version of the patch after the three dashes under your commit message. -- Sincerely, Johannes Löthberg PGP Key ID: 0x50FB9B273A9D0BB5 https://theos.kyriasis.com/~kyrias/
I'm so terribly sorry, I got a few errors over here that I thought meant that they didn't send. Turns out they did and I sent many different versions of the patch. It looks like the one you replied to here was the one I would say is the current one, but all the ones I sent have the same changes. Once again, I'm sorry for spamming the mailing list. I'll pay more attention next time. Mark Weiman On Thu, 2015-12-03 at 22:57 +0100, Johannes Löthberg wrote:
On 03/12, Mark Weiman wrote:
Adds functions and credential information to pin comments before others.
This needs two extra columns (PinnedTS and PinnedUsersID) to the PackageComments table.
Implements FS#10863
Signed-off-by: Mark Weiman <mark.weiman@markzz.com> ---
Two quick notes:
1) You sent something you called v2 twice, which is a bit confusing. 2) It would be useful if you put a comment on what changed from the last version of the patch after the three dashes under your commit message.
Adds capability to pin comments before others. Implements FS#10863 Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- schema/aur-schema.sql | 1 + upgrading/4.2.0.txt | 6 +++ web/html/css/aurweb.css | 6 +-- web/html/images/pin.min.svg | 1 + web/html/images/pin.svg | 3 ++ web/html/images/unpin.min.svg | 1 + web/html/images/unpin.svg | 4 ++ web/html/index.php | 2 + web/html/pkgbase.php | 4 ++ web/lib/credentials.inc.php | 2 + web/lib/pkgbasefuncs.inc.php | 93 ++++++++++++++++++++++++++++++++++++++++--- web/lib/pkgfuncs.inc.php | 51 +++++++++++++++++++++++- web/template/pkg_comments.php | 54 ++++++++++++++++++++----- 13 files changed, 207 insertions(+), 21 deletions(-) create mode 100755 web/html/images/pin.min.svg create mode 100755 web/html/images/pin.svg create mode 100755 web/html/images/unpin.min.svg create mode 100755 web/html/images/unpin.svg diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql index 315a75c..5561278 100644 --- a/schema/aur-schema.sql +++ b/schema/aur-schema.sql @@ -261,6 +261,7 @@ CREATE TABLE PackageComments ( EditedTS BIGINT UNSIGNED NULL DEFAULT NULL, EditedUsersID INTEGER UNSIGNED NULL DEFAULT NULL, DelUsersID INTEGER UNSIGNED NULL DEFAULT NULL, + PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (ID), INDEX (UsersID), INDEX (PackageBaseID), diff --git a/upgrading/4.2.0.txt b/upgrading/4.2.0.txt index 1f92ec5..2b3f983 100644 --- a/upgrading/4.2.0.txt +++ b/upgrading/4.2.0.txt @@ -15,3 +15,9 @@ CREATE UNIQUE INDEX ProviderNameProvides ON OfficialProviders (Name, Provides); ---- ALTER TABLE Users MODIFY Email VARCHAR(254) NOT NULL; ---- + +3. Add new column in PackageComments for pinning system. + +---- +ALTER TABLE PackageComments ADD PinnedTS bigint(20) DEFAULT 0; +---- diff --git a/web/html/css/aurweb.css b/web/html/css/aurweb.css index 11af747..82b83d9 100644 --- a/web/html/css/aurweb.css +++ b/web/html/css/aurweb.css @@ -101,7 +101,7 @@ color: #999; } -.delete-comment-form, .edit-comment { +.delete-comment-form, .pin-comment-form, .edit-comment { float: right; margin-left: 8px; } @@ -112,13 +112,13 @@ top: 1px; } -.delete-comment, .edit-comment { +.delete-comment, .edit-comment, .pin-comment { -webkit-filter: grayscale(100%); filter: grayscale(100%); opacity: 0.6; } -.delete-comment:hover, .edit-comment:hover { +.delete-comment:hover, .edit-comment:hover, .pin-comment:hover { -webkit-filter: none; filter: none; opacity: 1; diff --git a/web/html/images/pin.min.svg b/web/html/images/pin.min.svg new file mode 100755 index 0000000..ac08903 --- /dev/null +++ b/web/html/images/pin.min.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /></svg> diff --git a/web/html/images/pin.svg b/web/html/images/pin.svg new file mode 100755 index 0000000..b4ee9eb --- /dev/null +++ b/web/html/images/pin.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"> + <path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /> +</svg> diff --git a/web/html/images/unpin.min.svg b/web/html/images/unpin.min.svg new file mode 100755 index 0000000..3cf2413 --- /dev/null +++ b/web/html/images/unpin.min.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /><path style="fill:#3366aa;fill-opacity:1" d="m6.4 0l1 1-6.4 6.8-1-1z" /></svg> diff --git a/web/html/images/unpin.svg b/web/html/images/unpin.svg new file mode 100755 index 0000000..de89715 --- /dev/null +++ b/web/html/images/unpin.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"> + <path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /> + <path style="fill:#3366aa;fill-opacity:1" d="m6.4 0l1 1 -6.4 6.8 -1 -1z" /> +</svg> diff --git a/web/html/index.php b/web/html/index.php index ec99bb7..e99d22f 100644 --- a/web/html/index.php +++ b/web/html/index.php @@ -182,6 +182,8 @@ if (!empty($tokens[1]) && '/' . $tokens[1] == get_pkg_route()) { break; case "/images/x.min.svg": case "/images/pencil.min.svg": + case "/images/pin.min.svg": + case "/images/unpin.min.svg": header("Content-Type: image/svg+xml"); readfile("./$path"); break; diff --git a/web/html/pkgbase.php b/web/html/pkgbase.php index cbbf3cc..3ca6e55 100644 --- a/web/html/pkgbase.php +++ b/web/html/pkgbase.php @@ -99,6 +99,10 @@ if (check_token()) { list($ret, $output) = pkgbase_notify($ids, false); } elseif (current_action("do_DeleteComment")) { list($ret, $output) = pkgbase_delete_comment(); + } elseif (current_action("do_PinComment")) { + list($ret, $output) = pkgbase_pin_comment(); + } elseif (current_action("do_UnpinComment")) { + list($ret, $output) = pkgbase_unpin_comment(); } elseif (current_action("do_SetKeywords")) { list($ret, $output) = pkgbase_set_keywords($base_id, preg_split("/[\s,;]+/", $_POST['keywords'], -1, PREG_SPLIT_NO_EMPTY)); } elseif (current_action("do_FileRequest")) { diff --git a/web/lib/credentials.inc.php b/web/lib/credentials.inc.php index 648d78c..71bf5ff 100644 --- a/web/lib/credentials.inc.php +++ b/web/lib/credentials.inc.php @@ -8,6 +8,7 @@ define("CRED_ACCOUNT_SEARCH", 5); define("CRED_COMMENT_DELETE", 6); define("CRED_COMMENT_VIEW_DELETED", 22); define("CRED_COMMENT_EDIT", 25); +define("CRED_COMMENT_PIN", 26); define("CRED_PKGBASE_ADOPT", 7); define("CRED_PKGBASE_SET_KEYWORDS", 8); define("CRED_PKGBASE_DELETE", 9); @@ -60,6 +61,7 @@ function has_credential($credential, $approved_users=array()) { case CRED_COMMENT_DELETE: case CRED_COMMENT_VIEW_DELETED: case CRED_COMMENT_EDIT: + case CRED_COMMENT_PIN: case CRED_PKGBASE_ADOPT: case CRED_PKGBASE_SET_KEYWORDS: case CRED_PKGBASE_DELETE: diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php index afccc7d..89ea09e 100644 --- a/web/lib/pkgbasefuncs.inc.php +++ b/web/lib/pkgbasefuncs.inc.php @@ -7,10 +7,11 @@ include_once("pkgreqfuncs.inc.php"); * * @param string $base_id The package base ID to get comment count for * @param bool $include_deleted True if deleted comments should be included + * @param bool $only_pinned True if only pinned comments should be included * * @return string The number of comments left for a specific package */ -function pkgbase_comments_count($base_id, $include_deleted) { +function pkgbase_comments_count($base_id, $include_deleted, $only_pinned=false) { $base_id = intval($base_id); if (!$base_id) { return null; @@ -20,7 +21,10 @@ function pkgbase_comments_count($base_id, $include_deleted) { $q = "SELECT COUNT(*) FROM PackageComments "; $q.= "WHERE PackageBaseID = " . $base_id . " "; if (!$include_deleted) { - $q.= "AND DelUsersID IS NULL"; + $q.= "AND DelUsersID IS NULL "; + } + if ($only_pinned) { + $q.= "AND NOT PinnedTS = 0"; } $result = $dbh->query($q); if (!$result) { @@ -36,10 +40,11 @@ function pkgbase_comments_count($base_id, $include_deleted) { * @param int $base_id The package base ID to get comments for * @param int $limit Maximum number of comments to return (0 means unlimited) * @param bool $include_deleted True if deleted comments should be included + * @param bool $only_pinned True when only pinned comments are to be included * * @return array All package comment information for a specific package base */ -function pkgbase_comments($base_id, $limit, $include_deleted) { +function pkgbase_comments($base_id, $limit, $include_deleted, $only_pinned=false) { $base_id = intval($base_id); $limit = intval($limit); if (!$base_id) { @@ -48,12 +53,17 @@ function pkgbase_comments($base_id, $limit, $include_deleted) { $dbh = DB::connect(); $q = "SELECT PackageComments.ID, A.UserName AS UserName, UsersID, Comments, "; - $q.= "CommentTS, EditedTS, B.UserName AS EditUserName, "; - $q.= "DelUsersID, C.UserName AS DelUserName FROM PackageComments "; + $q.= "PackageBaseID, CommentTS, EditedTS, B.UserName AS EditUserName, "; + $q.= "DelUsersID, C.UserName AS DelUserName, "; + $q.= "PinnedTS FROM PackageComments "; $q.= "LEFT JOIN Users A ON PackageComments.UsersID = A.ID "; $q.= "LEFT JOIN Users B ON PackageComments.EditedUsersID = B.ID "; $q.= "LEFT JOIN Users C ON PackageComments.DelUsersID = C.ID "; $q.= "WHERE PackageBaseID = " . $base_id . " "; + + if ($only_pinned) { + $q.= "AND NOT PinnedTS = 0 "; + } if (!$include_deleted) { $q.= "AND DelUsersID IS NULL "; } @@ -111,6 +121,69 @@ function pkgbase_add_comment($base_id, $uid, $comment) { } /** + * Pin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_pin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + if (pkgbase_comments_count($_POST['package_base'], false, true) >= 5){ + return array(false, __("No more than 5 comments can be pinned.")); + } + + if (!can_pin_comment($comment_id)) { + return array(false, __("You are not allowed to pin this comment.")); + } + + $dbh = DB::connect(); + $q = "UPDATE PackageComments "; + $q.= "SET PinnedTS = UNIX_TIMESTAMP() "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been pinned.")); +} + +/** + * Unpin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_unpin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + if (!can_pin_comment($comment_id)) { + return array(false, __("You are not allowed to unpin this comment.")); + } + + $dbh = DB::connect(); + $q = "UPDATE PackageComments "; + $q.= "SET PinnedTS = 0 "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been unpinned.")); +} + +/** + * Get a list of all packages a logged-in user has voted for * * @param string $sid The session ID of the visitor @@ -196,9 +269,17 @@ function pkgbase_display_details($base_id, $row, $SID="") { if ($SID) { include('pkg_comment_box.php'); } + + $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } $limit = isset($_GET['comments']) ? 0 : 10; - $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/lib/pkgfuncs.inc.php b/web/lib/pkgfuncs.inc.php index cedc360..c0ed99c 100644 --- a/web/lib/pkgfuncs.inc.php +++ b/web/lib/pkgfuncs.inc.php @@ -83,6 +83,47 @@ function can_edit_comment_array($comment) { } /** + * Determine if the user can pin a specific package comment + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the backend side of comment pinning. + * + * @param string $comment_id The comment ID in the database + * + * @return bool True if the user can pin the comment, otherwise false + */ +function can_pin_comment($comment_id=0) { + $dbh = DB::connect(); + + $q = "SELECT MaintainerUID FROM PackageBases AS pb "; + $q.= "LEFT JOIN PackageComments AS pc ON pb.ID = pc.PackageBaseID "; + $q.= "WHERE pc.ID = " . intval($comment_id); + $result = $dbh->query($q); + + if (!$result) { + return false; + } + + $uid = $result->fetch(PDO::FETCH_COLUMN, 0); + + return has_credential(CRED_COMMENT_PIN, array($uid)); +} + +/** + * Determine if the user can edit a specific package comment using an array + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the frontend side of comment pinning. + * + * @param array $comment All database information relating a specific comment + * + * @return bool True if the user can edit the comment, otherwise false + */ +function can_pin_comment_array($comment) { + return can_pin_comment($comment['ID']); +} + +/** * Check to see if the package name already exists in the database * * @param string $name The package name to check @@ -581,9 +622,17 @@ function pkg_display_details($id=0, $row, $SID="") { if ($SID) { include('pkg_comment_box.php'); } + + $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } $limit = isset($_GET['comments']) ? 0 : 10; - $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/template/pkg_comments.php b/web/template/pkg_comments.php index 21ce16f..dd71b93 100644 --- a/web/template/pkg_comments.php +++ b/web/template/pkg_comments.php @@ -1,18 +1,27 @@ <?php -if (isset($row['BaseID'])) { - /* On a package details page. */ - $base_id = $row['BaseID']; -} else { - /* On a package base details page. */ - $base_id = $row['ID']; +if (!isset($base_id)) { + if (isset($row['BaseID'])) { + /* On a package details page. */ + $base_id = $row['BaseID']; + } else { + /* On a package base details page. */ + $base_id = $row['ID']; + } +} +if (!isset($count)) { + $count = pkgbase_comments_count($base_id, $include_deleted); } -$include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); -$count = pkgbase_comments_count($base_id, $include_deleted); ?> <div id="news"> <h3> - <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> - <span class="arrow"></span> + <?php if (!isset($comments)): ?> + <?php $comments = $pinned ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Pinned Comments') ?></a> + <span class="arrow"></span> + <?php else: ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> + <span class="arrow"></span> + <?php endif; ?> </h3> <?php while (list($indx, $row) = each($comments)): ?> @@ -56,6 +65,29 @@ $count = pkgbase_comments_count($base_id, $include_deleted); <?php if (!$row['DelUsersID'] && can_edit_comment_array($row)): ?> <a href="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name) . 'edit-comment/?comment_id=' . $row['ID'], ENT_QUOTES) ?>" class="edit-comment" title="<?= __('Edit comment') ?>"><img src="/images/pencil.min.svg" alt="<?= __('Edit comment') ?>" width="11" height="11"></a> <?php endif; ?> + + <?php if (!$row['DelUsersID'] && !$row['PinnedTS'] && can_pin_comment_array($row)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_PinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="package_base" value="<?= $base_id ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="image" class="pin-comment" src="/images/pin.min.svg" width="11" height="11" alt="<?= __('Pin comment') ?>" title="<?= __('Pin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> + + <?php if (!$row['DelUsersID'] && $row['PinnedTS'] && can_pin_comment_array($row)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_UnpinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="image" class="pin-comment" src="/images/unpin.min.svg" width="11" height="11" alt="<?= __('Unpin comment') ?>" title="<?= __('Unpin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> </h4> <div class="article-content<?php if ($row['DelUsersID']): ?> comment-deleted<?php endif; ?>"> <p> @@ -64,7 +96,7 @@ $count = pkgbase_comments_count($base_id, $include_deleted); </div> <?php endwhile; ?> -<?php if ($count > 10 && !isset($_GET['comments'])): ?> +<?php if ($count > 10 && !isset($_GET['comments']) && !isset($pinned)): ?> <h3> <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments') ?> (<?= $count ?>)"><?= __('All comments', $count) ?></a> </h3> -- 2.6.3
On Wed, 09 Dec 2015 at 16:08:39, Mark Weiman wrote:
Adds capability to pin comments before others.
Implements FS#10863
Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- schema/aur-schema.sql | 1 + upgrading/4.2.0.txt | 6 +++ web/html/css/aurweb.css | 6 +-- web/html/images/pin.min.svg | 1 + web/html/images/pin.svg | 3 ++ web/html/images/unpin.min.svg | 1 + web/html/images/unpin.svg | 4 ++ web/html/index.php | 2 + web/html/pkgbase.php | 4 ++ web/lib/credentials.inc.php | 2 + web/lib/pkgbasefuncs.inc.php | 93 ++++++++++++++++++++++++++++++++++++++++--- web/lib/pkgfuncs.inc.php | 51 +++++++++++++++++++++++- web/template/pkg_comments.php | 54 ++++++++++++++++++++----- 13 files changed, 207 insertions(+), 21 deletions(-)
This version already looks pretty good! There are still some whitespace errors. You should see error messages like .git/rebase-apply/patch:212: trailing whitespace. when you try to apply the patch yourself, e.g. by running $ git format-patch HEAD^ $ git reset --hard HEAD^ $ git am 0001-Implement-capability-to-pin-comments-above-others.patch More comments below.
create mode 100755 web/html/images/pin.min.svg create mode 100755 web/html/images/pin.svg create mode 100755 web/html/images/unpin.min.svg create mode 100755 web/html/images/unpin.svg
Why are these files executable?
diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql index 315a75c..5561278 100644 --- a/schema/aur-schema.sql +++ b/schema/aur-schema.sql @@ -261,6 +261,7 @@ CREATE TABLE PackageComments ( EditedTS BIGINT UNSIGNED NULL DEFAULT NULL, EditedUsersID INTEGER UNSIGNED NULL DEFAULT NULL, DelUsersID INTEGER UNSIGNED NULL DEFAULT NULL, + PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (ID), INDEX (UsersID), INDEX (PackageBaseID), diff --git a/upgrading/4.2.0.txt b/upgrading/4.2.0.txt index 1f92ec5..2b3f983 100644 --- a/upgrading/4.2.0.txt +++ b/upgrading/4.2.0.txt @@ -15,3 +15,9 @@ CREATE UNIQUE INDEX ProviderNameProvides ON OfficialProviders (Name, Provides); ---- ALTER TABLE Users MODIFY Email VARCHAR(254) NOT NULL; ---- + +3. Add new column in PackageComments for pinning system. + +---- +ALTER TABLE PackageComments ADD PinnedTS bigint(20) DEFAULT 0;
Why is this different from the schema line above? ALTER TABLE PackageComments ADD COLUMN PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0; is what I would have expected here.
[...] /** + * Pin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_pin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + if (pkgbase_comments_count($_POST['package_base'], false, true) >= 5){ + return array(false, __("No more than 5 comments can be pinned.")); + } + + if (!can_pin_comment($comment_id)) { + return array(false, __("You are not allowed to pin this comment.")); + } + + $dbh = DB::connect(); + $q = "UPDATE PackageComments "; + $q.= "SET PinnedTS = UNIX_TIMESTAMP() "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been pinned.")); +} + +/** + * Unpin a package comment + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_unpin_comment() { + $uid = uid_from_sid($_COOKIE["AURSID"]); + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + if (!can_pin_comment($comment_id)) { + return array(false, __("You are not allowed to unpin this comment.")); + } + + $dbh = DB::connect(); + $q = "UPDATE PackageComments "; + $q.= "SET PinnedTS = 0 "; + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + return array(true, __("Comment has been unpinned.")); +} + +/** +
Seems like pkgbase_pin_comment() and pkgbase_unpin_comment() are almost identical. Can we share most of the code? Also, we make sure that one cannot pin more than five comments but I would have expected the "Pin" icons to be hidden when the limit is reached. The current UI behavior is quite confusing.
[...] @@ -581,9 +622,17 @@ function pkg_display_details($id=0, $row, $SID="") { if ($SID) { include('pkg_comment_box.php'); } + + $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + }
This looks a bit weird. Wrong indentation?
$limit = isset($_GET['comments']) ? 0 : 10; - $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/template/pkg_comments.php b/web/template/pkg_comments.php index 21ce16f..dd71b93 100644 --- a/web/template/pkg_comments.php +++ b/web/template/pkg_comments.php @@ -1,18 +1,27 @@ <?php -if (isset($row['BaseID'])) { - /* On a package details page. */ - $base_id = $row['BaseID']; -} else { - /* On a package base details page. */ - $base_id = $row['ID']; +if (!isset($base_id)) { + if (isset($row['BaseID'])) { + /* On a package details page. */ + $base_id = $row['BaseID']; + } else { + /* On a package base details page. */ + $base_id = $row['ID']; + } +} [...]
Why is this needed? Could you elaborate please? Thank you for all your hard work on implementing this! Regards, Lukas
On Fri, 2015-12-11 at 20:42 +0100, Lukas Fleischer wrote:
This version already looks pretty good! There are still some whitespace errors. You should see error messages like
.git/rebase-apply/patch:212: trailing whitespace.
when you try to apply the patch yourself, e.g. by running
$ git format-patch HEAD^ $ git reset --hard HEAD^ $ git am 0001-Implement-capability-to-pin-comments-above- others.patch
More comments below.
Why are these files executable?
I have no idea how or why they are and it must have slipped past my eyes when I checked thdiff --git a/schema/aur-schema.sql b/schema/aur- schema.sql
Why is this different from the schema line above?
ALTER TABLE PackageComments ADD COLUMN PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0;
is what I would have expected here.
I will change it to your suggestion.
Seems like pkgbase_pin_comment() and pkgbase_unpin_comment() are almost identical. Can we share most of the code?
I thought about doing that, but I thought the names of the functions were important for code readability. Perhaps adding an argument to pkgbase_pin_comment() that does it and just have pkgbase_unpin_comment() call the other function to change that argument? Also the error messages have a slight difference in text that also pushed me to making two separate functions.
Also, we make sure that one cannot pin more than five comments but I would have expected the "Pin" icons to be hidden when the limit is reached. The current UI behavior is quite confusing.
I'll change this.
This looks a bit weird. Wrong indentation?
Gedit seems to enjoy changing my tab settings whenever I close it, I will be double sure to check it when I use it in the future. I'll do another sweep to see if there are more space indentations rather than tab indentations. (UGH)
Why is this needed? Could you elaborate please?
The reason I had to do this is the variable $base_id when opening the file for a second time (to display all comments) was already set and caused it to then be set to an empty string, so I added a check to see if the variable was already set so if it is, it can avoid trying to set it again removing the problem. I do not really understand why this happens, but it does on my computer. It seriously took me a couple of hours of head scratching to find that problem and fix it because it really shouldn't have been an issue. I also noticed that $base_id is set in both pkgbase_display_details() and pkg_display_details(), should I just remove that assignment altogether in pkg_comments.php?
Thank you for all your hard work on implementing this!
Regards, Lukas
Mark Weiman
On Fri, 11 Dec 2015 at 21:46:58, Mark Weiman wrote:
[...]
Seems like pkgbase_pin_comment() and pkgbase_unpin_comment() are almost identical. Can we share most of the code?
I thought about doing that, but I thought the names of the functions were important for code readability. Perhaps adding an argument to pkgbase_pin_comment() that does it and just have pkgbase_unpin_comment() call the other function to change that argument? Also the error messages have a slight difference in text that also pushed me to making two separate functions.
You might want to have a look at pkgbase_adopt() and pkgbase_vote() which do something similar.
[...]
Why is this needed? Could you elaborate please?
The reason I had to do this is the variable $base_id when opening the file for a second time (to display all comments) was already set and caused it to then be set to an empty string, so I added a check to see if the variable was already set so if it is, it can avoid trying to set it again removing the problem. I do not really understand why this happens, but it does on my computer.
It seriously took me a couple of hours of head scratching to find that problem and fix it because it really shouldn't have been an issue.
I also noticed that $base_id is set in both pkgbase_display_details() and pkg_display_details(), should I just remove that assignment altogether in pkg_comments.php? [...]
Yes, if removing this code doesn't break anything, it is definitely something we want to do. Maybe do it in a preparatory patch since it is quite unrelated to what this patch does. Thanks!
Removes reassignment of $base_id in web/template/pkg_comments.php as it is assigned in both pkgbase_display_details() and pkg_display_details(). Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- web/template/pkg_comments.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/template/pkg_comments.php b/web/template/pkg_comments.php index 21ce16f..15547ce 100644 --- a/web/template/pkg_comments.php +++ b/web/template/pkg_comments.php @@ -1,11 +1,4 @@ <?php -if (isset($row['BaseID'])) { - /* On a package details page. */ - $base_id = $row['BaseID']; -} else { - /* On a package base details page. */ - $base_id = $row['ID']; -} $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); $count = pkgbase_comments_count($base_id, $include_deleted); ?> -- 2.6.4
Adds capability to pin comments before others. Implements FS#10863 Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- schema/aur-schema.sql | 1 + upgrading/4.2.0.txt | 6 +++ web/html/css/aurweb.css | 6 +-- web/html/images/pin.min.svg | 1 + web/html/images/pin.svg | 3 ++ web/html/images/unpin.min.svg | 1 + web/html/images/unpin.svg | 4 ++ web/html/index.php | 2 + web/html/pkgbase.php | 4 ++ web/lib/credentials.inc.php | 2 + web/lib/pkgbasefuncs.inc.php | 85 ++++++++++++++++++++++++++++++++++++++++--- web/lib/pkgfuncs.inc.php | 51 +++++++++++++++++++++++++- web/template/pkg_comments.php | 40 +++++++++++++++++--- 13 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 web/html/images/pin.min.svg create mode 100644 web/html/images/pin.svg create mode 100644 web/html/images/unpin.min.svg create mode 100644 web/html/images/unpin.svg diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql index 315a75c..5561278 100644 --- a/schema/aur-schema.sql +++ b/schema/aur-schema.sql @@ -261,6 +261,7 @@ CREATE TABLE PackageComments ( EditedTS BIGINT UNSIGNED NULL DEFAULT NULL, EditedUsersID INTEGER UNSIGNED NULL DEFAULT NULL, DelUsersID INTEGER UNSIGNED NULL DEFAULT NULL, + PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (ID), INDEX (UsersID), INDEX (PackageBaseID), diff --git a/upgrading/4.2.0.txt b/upgrading/4.2.0.txt index 1f92ec5..1450d5e 100644 --- a/upgrading/4.2.0.txt +++ b/upgrading/4.2.0.txt @@ -15,3 +15,9 @@ CREATE UNIQUE INDEX ProviderNameProvides ON OfficialProviders (Name, Provides); ---- ALTER TABLE Users MODIFY Email VARCHAR(254) NOT NULL; ---- + +3. Add new column in PackageComments for pinning system. + +---- +ALTER TABLE PackageComments ADD COLUMN PinnedTS BIGINT UNSIGNED NOT NULL DEFAULT 0; +---- diff --git a/web/html/css/aurweb.css b/web/html/css/aurweb.css index 11af747..82b83d9 100644 --- a/web/html/css/aurweb.css +++ b/web/html/css/aurweb.css @@ -101,7 +101,7 @@ color: #999; } -.delete-comment-form, .edit-comment { +.delete-comment-form, .pin-comment-form, .edit-comment { float: right; margin-left: 8px; } @@ -112,13 +112,13 @@ top: 1px; } -.delete-comment, .edit-comment { +.delete-comment, .edit-comment, .pin-comment { -webkit-filter: grayscale(100%); filter: grayscale(100%); opacity: 0.6; } -.delete-comment:hover, .edit-comment:hover { +.delete-comment:hover, .edit-comment:hover, .pin-comment:hover { -webkit-filter: none; filter: none; opacity: 1; diff --git a/web/html/images/pin.min.svg b/web/html/images/pin.min.svg new file mode 100644 index 0000000..ac08903 --- /dev/null +++ b/web/html/images/pin.min.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /></svg> diff --git a/web/html/images/pin.svg b/web/html/images/pin.svg new file mode 100644 index 0000000..b4ee9eb --- /dev/null +++ b/web/html/images/pin.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"> + <path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /> +</svg> diff --git a/web/html/images/unpin.min.svg b/web/html/images/unpin.min.svg new file mode 100644 index 0000000..3cf2413 --- /dev/null +++ b/web/html/images/unpin.min.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"><path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /><path style="fill:#3366aa;fill-opacity:1" d="m6.4 0l1 1-6.4 6.8-1-1z" /></svg> diff --git a/web/html/images/unpin.svg b/web/html/images/unpin.svg new file mode 100644 index 0000000..de89715 --- /dev/null +++ b/web/html/images/unpin.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"> + <path style="fill:#3366aa;fill-opacity:1" d="M1.34 0a.5.5 0 0 0 .16 1h.5v2h-1c-.55 0-1 .45-1 1h3v3l.44 1 .56-1v-3h3c0-.55-.45-1-1-1h-1v-2h.5a.5.5 0 1 0 0-1h-4a.5.5 0 0 0-.09 0 .5.5 0 0 0-.06 0z" /> + <path style="fill:#3366aa;fill-opacity:1" d="m6.4 0l1 1 -6.4 6.8 -1 -1z" /> +</svg> diff --git a/web/html/index.php b/web/html/index.php index ec99bb7..e99d22f 100644 --- a/web/html/index.php +++ b/web/html/index.php @@ -182,6 +182,8 @@ if (!empty($tokens[1]) && '/' . $tokens[1] == get_pkg_route()) { break; case "/images/x.min.svg": case "/images/pencil.min.svg": + case "/images/pin.min.svg": + case "/images/unpin.min.svg": header("Content-Type: image/svg+xml"); readfile("./$path"); break; diff --git a/web/html/pkgbase.php b/web/html/pkgbase.php index cbbf3cc..45b8084 100644 --- a/web/html/pkgbase.php +++ b/web/html/pkgbase.php @@ -99,6 +99,10 @@ if (check_token()) { list($ret, $output) = pkgbase_notify($ids, false); } elseif (current_action("do_DeleteComment")) { list($ret, $output) = pkgbase_delete_comment(); + } elseif (current_action("do_PinComment")) { + list($ret, $output) = pkgbase_pin_comment(); + } elseif (current_action("do_UnpinComment")) { + list($ret, $output) = pkgbase_pin_comment(true); } elseif (current_action("do_SetKeywords")) { list($ret, $output) = pkgbase_set_keywords($base_id, preg_split("/[\s,;]+/", $_POST['keywords'], -1, PREG_SPLIT_NO_EMPTY)); } elseif (current_action("do_FileRequest")) { diff --git a/web/lib/credentials.inc.php b/web/lib/credentials.inc.php index 648d78c..71bf5ff 100644 --- a/web/lib/credentials.inc.php +++ b/web/lib/credentials.inc.php @@ -8,6 +8,7 @@ define("CRED_ACCOUNT_SEARCH", 5); define("CRED_COMMENT_DELETE", 6); define("CRED_COMMENT_VIEW_DELETED", 22); define("CRED_COMMENT_EDIT", 25); +define("CRED_COMMENT_PIN", 26); define("CRED_PKGBASE_ADOPT", 7); define("CRED_PKGBASE_SET_KEYWORDS", 8); define("CRED_PKGBASE_DELETE", 9); @@ -60,6 +61,7 @@ function has_credential($credential, $approved_users=array()) { case CRED_COMMENT_DELETE: case CRED_COMMENT_VIEW_DELETED: case CRED_COMMENT_EDIT: + case CRED_COMMENT_PIN: case CRED_PKGBASE_ADOPT: case CRED_PKGBASE_SET_KEYWORDS: case CRED_PKGBASE_DELETE: diff --git a/web/lib/pkgbasefuncs.inc.php b/web/lib/pkgbasefuncs.inc.php index afccc7d..91160d3 100644 --- a/web/lib/pkgbasefuncs.inc.php +++ b/web/lib/pkgbasefuncs.inc.php @@ -7,10 +7,11 @@ include_once("pkgreqfuncs.inc.php"); * * @param string $base_id The package base ID to get comment count for * @param bool $include_deleted True if deleted comments should be included + * @param bool $only_pinned True if only pinned comments should be included * * @return string The number of comments left for a specific package */ -function pkgbase_comments_count($base_id, $include_deleted) { +function pkgbase_comments_count($base_id, $include_deleted, $only_pinned=false) { $base_id = intval($base_id); if (!$base_id) { return null; @@ -20,7 +21,10 @@ function pkgbase_comments_count($base_id, $include_deleted) { $q = "SELECT COUNT(*) FROM PackageComments "; $q.= "WHERE PackageBaseID = " . $base_id . " "; if (!$include_deleted) { - $q.= "AND DelUsersID IS NULL"; + $q.= "AND DelUsersID IS NULL "; + } + if ($only_pinned) { + $q.= "AND NOT PinnedTS = 0"; } $result = $dbh->query($q); if (!$result) { @@ -36,10 +40,11 @@ function pkgbase_comments_count($base_id, $include_deleted) { * @param int $base_id The package base ID to get comments for * @param int $limit Maximum number of comments to return (0 means unlimited) * @param bool $include_deleted True if deleted comments should be included + * @param bool $only_pinned True when only pinned comments are to be included * * @return array All package comment information for a specific package base */ -function pkgbase_comments($base_id, $limit, $include_deleted) { +function pkgbase_comments($base_id, $limit, $include_deleted, $only_pinned=false) { $base_id = intval($base_id); $limit = intval($limit); if (!$base_id) { @@ -48,12 +53,17 @@ function pkgbase_comments($base_id, $limit, $include_deleted) { $dbh = DB::connect(); $q = "SELECT PackageComments.ID, A.UserName AS UserName, UsersID, Comments, "; - $q.= "CommentTS, EditedTS, B.UserName AS EditUserName, "; - $q.= "DelUsersID, C.UserName AS DelUserName FROM PackageComments "; + $q.= "PackageBaseID, CommentTS, EditedTS, B.UserName AS EditUserName, "; + $q.= "DelUsersID, C.UserName AS DelUserName, "; + $q.= "PinnedTS FROM PackageComments "; $q.= "LEFT JOIN Users A ON PackageComments.UsersID = A.ID "; $q.= "LEFT JOIN Users B ON PackageComments.EditedUsersID = B.ID "; $q.= "LEFT JOIN Users C ON PackageComments.DelUsersID = C.ID "; $q.= "WHERE PackageBaseID = " . $base_id . " "; + + if ($only_pinned) { + $q.= "AND NOT PinnedTS = 0 "; + } if (!$include_deleted) { $q.= "AND DelUsersID IS NULL "; } @@ -111,6 +121,61 @@ function pkgbase_add_comment($base_id, $uid, $comment) { } /** + * Pin/unpin a package comment + * + * @param bool $unpin True if unpinning rather than pinning + * + * @return array Tuple of success/failure indicator and error message + */ +function pkgbase_pin_comment($unpin=false) { + $uid = uid_from_sid($_COOKIE["AURSID"]); + + if (!$uid) { + return array(false, __("You must be logged in before you can edit package information.")); + } + + if (isset($_POST["comment_id"])) { + $comment_id = $_POST["comment_id"]; + } else { + return array(false, __("Missing comment ID.")); + } + + if (!$unpin) { + if (pkgbase_comments_count($_POST['package_base'], false, true) >= 5){ + return array(false, __("No more than 5 comments can be pinned.")); + } + } + + if (!can_pin_comment($comment_id)) { + if (!$unpin) { + return array(false, __("You are not allowed to pin this comment.")); + } + else { + return array(false, __("You are not allowed to unpin this comment.")); + } + } + + $dbh = DB::connect(); + $q = "UPDATE PackageComments "; + if (!$unpin) { + $q.= "SET PinnedTS = UNIX_TIMESTAMP() "; + } + else { + $q.= "SET PinnedTS = 0 "; + } + $q.= "WHERE ID = ".intval($comment_id); + $dbh->exec($q); + + if (!$unpin) { + return array(true, __("Comment has been pinned.")); + } + else { + return array(true, __("Comment has been unpinned.")); + } +} + +/** + * Get a list of all packages a logged-in user has voted for * * @param string $sid The session ID of the visitor @@ -197,8 +262,16 @@ function pkgbase_display_details($base_id, $row, $SID="") { include('pkg_comment_box.php'); } - $limit = isset($_GET['comments']) ? 0 : 10; $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } + + $limit = isset($_GET['comments']) ? 0 : 10; $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/lib/pkgfuncs.inc.php b/web/lib/pkgfuncs.inc.php index cedc360..c2bbe38 100644 --- a/web/lib/pkgfuncs.inc.php +++ b/web/lib/pkgfuncs.inc.php @@ -83,6 +83,47 @@ function can_edit_comment_array($comment) { } /** + * Determine if the user can pin a specific package comment + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the backend side of comment pinning. + * + * @param string $comment_id The comment ID in the database + * + * @return bool True if the user can pin the comment, otherwise false + */ +function can_pin_comment($comment_id=0) { + $dbh = DB::connect(); + + $q = "SELECT MaintainerUID FROM PackageBases AS pb "; + $q.= "LEFT JOIN PackageComments AS pc ON pb.ID = pc.PackageBaseID "; + $q.= "WHERE pc.ID = " . intval($comment_id); + $result = $dbh->query($q); + + if (!$result) { + return false; + } + + $uid = $result->fetch(PDO::FETCH_COLUMN, 0); + + return has_credential(CRED_COMMENT_PIN, array($uid)); +} + +/** + * Determine if the user can edit a specific package comment using an array + * + * Only the Package Maintainer, Trusted Users, and Developers can pin + * comments. This function is used for the frontend side of comment pinning. + * + * @param array $comment All database information relating a specific comment + * + * @return bool True if the user can edit the comment, otherwise false + */ +function can_pin_comment_array($comment) { + return can_pin_comment($comment['ID']); +} + +/** * Check to see if the package name already exists in the database * * @param string $name The package name to check @@ -582,8 +623,16 @@ function pkg_display_details($id=0, $row, $SID="") { include('pkg_comment_box.php'); } - $limit = isset($_GET['comments']) ? 0 : 10; $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); + + $limit_pinned = isset($_GET['pinned']) ? 0 : 5; + $pinned = pkgbase_comments($base_id, $limit_pinned, false, true); + if (!empty($pinned)) { + include('pkg_comments.php'); + unset($pinned); + } + + $limit = isset($_GET['comments']) ? 0 : 10; $comments = pkgbase_comments($base_id, $limit, $include_deleted); if (!empty($comments)) { include('pkg_comments.php'); diff --git a/web/template/pkg_comments.php b/web/template/pkg_comments.php index 15547ce..2046364 100644 --- a/web/template/pkg_comments.php +++ b/web/template/pkg_comments.php @@ -1,11 +1,18 @@ <?php -$include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED); -$count = pkgbase_comments_count($base_id, $include_deleted); +if (!isset($count)) { + $count = pkgbase_comments_count($base_id, $include_deleted); +} ?> <div id="news"> <h3> - <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> - <span class="arrow"></span> + <?php if (!isset($comments)): ?> + <?php $comments = $pinned ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Pinned Comments') ?></a> + <span class="arrow"></span> + <?php else: ?> + <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments' , $count) ?> (<?= $count ?>)"><?= __('Latest Comments') ?></a> + <span class="arrow"></span> + <?php endif; ?> </h3> <?php while (list($indx, $row) = each($comments)): ?> @@ -49,6 +56,29 @@ $count = pkgbase_comments_count($base_id, $include_deleted); <?php if (!$row['DelUsersID'] && can_edit_comment_array($row)): ?> <a href="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name) . 'edit-comment/?comment_id=' . $row['ID'], ENT_QUOTES) ?>" class="edit-comment" title="<?= __('Edit comment') ?>"><img src="/images/pencil.min.svg" alt="<?= __('Edit comment') ?>" width="11" height="11"></a> <?php endif; ?> + + <?php if (!$row['DelUsersID'] && !$row['PinnedTS'] && can_pin_comment_array($row) && !(pkgbase_comments_count($base_id, false, true) >= 5)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_PinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="package_base" value="<?= $base_id ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="image" class="pin-comment" src="/images/pin.min.svg" width="11" height="11" alt="<?= __('Pin comment') ?>" title="<?= __('Pin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> + + <?php if (!$row['DelUsersID'] && $row['PinnedTS'] && can_pin_comment_array($row)): ?> + <form class="pin-comment-form" method="post" action="<?= htmlspecialchars(get_pkgbase_uri($pkgbase_name), ENT_QUOTES); ?>"> + <fieldset style="display:inline;"> + <input type="hidden" name="action" value="do_UnpinComment" /> + <input type="hidden" name="comment_id" value="<?= $row['ID'] ?>" /> + <input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" /> + <input type="image" class="pin-comment" src="/images/unpin.min.svg" width="11" height="11" alt="<?= __('Unpin comment') ?>" title="<?= __('Unpin comment') ?>" name="submit" value="1" /> + </fieldset> + </form> + <?php endif; ?> </h4> <div class="article-content<?php if ($row['DelUsersID']): ?> comment-deleted<?php endif; ?>"> <p> @@ -57,7 +87,7 @@ $count = pkgbase_comments_count($base_id, $include_deleted); </div> <?php endwhile; ?> -<?php if ($count > 10 && !isset($_GET['comments'])): ?> +<?php if ($count > 10 && !isset($_GET['comments']) && !isset($pinned)): ?> <h3> <a href="<?= htmlentities(get_pkgbase_uri($pkgbase_name), ENT_QUOTES) . '?' . mkurl('comments=all') ?>" title="<?= __('View all comments') ?> (<?= $count ?>)"><?= __('All comments', $count) ?></a> </h3> -- 2.6.4
On Sat, 12 Dec 2015 at 01:01:31, Mark Weiman wrote:
Adds capability to pin comments before others.
Implements FS#10863
Signed-off-by: Mark Weiman <mark.weiman@markzz.com> --- schema/aur-schema.sql | 1 + upgrading/4.2.0.txt | 6 +++ web/html/css/aurweb.css | 6 +-- web/html/images/pin.min.svg | 1 + web/html/images/pin.svg | 3 ++ web/html/images/unpin.min.svg | 1 + web/html/images/unpin.svg | 4 ++ web/html/index.php | 2 + web/html/pkgbase.php | 4 ++ web/lib/credentials.inc.php | 2 + web/lib/pkgbasefuncs.inc.php | 85 ++++++++++++++++++++++++++++++++++++++++--- web/lib/pkgfuncs.inc.php | 51 +++++++++++++++++++++++++- web/template/pkg_comments.php | 40 +++++++++++++++++--- 13 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 web/html/images/pin.min.svg create mode 100644 web/html/images/pin.svg create mode 100644 web/html/images/unpin.min.svg create mode 100644 web/html/images/unpin.svg [...]
Applied this with some minor stylistic changes. Thanks again!
participants (3)
-
Johannes Löthberg
-
Lukas Fleischer
-
Mark Weiman