Add link to flag OOD comment
[aur.git] / web / lib / pkgbasefuncs.inc.php
blob2b1201d5389fbc2500de08cd0ec5aca61310efaf
1 <?php
3 include_once("pkgreqfuncs.inc.php");
5 /**
6 * Get the number of non-deleted comments for a specific package base
8 * @param string $base_id The package base ID to get comment count for
9 * @param bool $include_deleted True if deleted comments should be included
10 * @param bool $only_pinned True if only pinned comments should be included
12 * @return string The number of comments left for a specific package
14 function pkgbase_comments_count($base_id, $include_deleted, $only_pinned=false) {
15 $base_id = intval($base_id);
16 if (!$base_id) {
17 return null;
20 $dbh = DB::connect();
21 $q = "SELECT COUNT(*) FROM PackageComments ";
22 $q.= "WHERE PackageBaseID = " . $base_id . " ";
23 if (!$include_deleted) {
24 $q.= "AND DelTS IS NULL";
26 if ($only_pinned) {
27 $q.= "AND NOT PinnedTS = 0";
29 $result = $dbh->query($q);
30 if (!$result) {
31 return null;
34 return $result->fetchColumn(0);
37 /**
38 * Get all package comment information for a specific package base
40 * @param int $base_id The package base ID to get comments for
41 * @param int $limit Maximum number of comments to return (0 means unlimited)
42 * @param bool $include_deleted True if deleted comments should be included
43 * @param bool $only_pinned True when only pinned comments are to be included
45 * @return array All package comment information for a specific package base
47 function pkgbase_comments($base_id, $limit, $include_deleted, $only_pinned=false) {
48 $base_id = intval($base_id);
49 $limit = intval($limit);
50 if (!$base_id) {
51 return null;
54 $dbh = DB::connect();
55 $q = "SELECT PackageComments.ID, A.UserName AS UserName, UsersID, Comments, ";
56 $q.= "PackageBaseID, CommentTS, DelTS, EditedTS, B.UserName AS EditUserName, ";
57 $q.= "DelUsersID, C.UserName AS DelUserName, ";
58 $q.= "PinnedTS FROM PackageComments ";
59 $q.= "LEFT JOIN Users A ON PackageComments.UsersID = A.ID ";
60 $q.= "LEFT JOIN Users B ON PackageComments.EditedUsersID = B.ID ";
61 $q.= "LEFT JOIN Users C ON PackageComments.DelUsersID = C.ID ";
62 $q.= "WHERE PackageBaseID = " . $base_id . " ";
64 if (!$include_deleted) {
65 $q.= "AND DelTS IS NULL ";
67 if ($only_pinned) {
68 $q.= "AND NOT PinnedTS = 0 ";
70 $q.= "ORDER BY CommentTS DESC";
71 if ($limit > 0) {
72 $q.=" LIMIT " . $limit;
74 $result = $dbh->query($q);
75 if (!$result) {
76 return null;
79 return $result->fetchAll();
82 /**
83 * Add a comment to a package page and send out appropriate notifications
85 * @param string $base_id The package base ID to add the comment on
86 * @param string $uid The user ID of the individual who left the comment
87 * @param string $comment The comment left on a package page
89 * @return void
91 function pkgbase_add_comment($base_id, $uid, $comment) {
92 $dbh = DB::connect();
94 if (trim($comment) == '') {
95 return array(false, __('Comment cannot be empty.'));
98 $q = "INSERT INTO PackageComments ";
99 $q.= "(PackageBaseID, UsersID, Comments, CommentTS) VALUES (";
100 $q.= intval($base_id) . ", " . $uid . ", ";
101 $q.= $dbh->quote($comment) . ", UNIX_TIMESTAMP())";
102 $dbh->exec($q);
103 $comment_id = $dbh->lastInsertId();
105 notify(array('comment', $uid, $base_id, $comment_id));
107 return array(true, __('Comment has been added.'));
111 * Pin/unpin a package comment
113 * @param bool $unpin True if unpinning rather than pinning
115 * @return array Tuple of success/failure indicator and error message
117 function pkgbase_pin_comment($unpin=false) {
118 $uid = uid_from_sid($_COOKIE["AURSID"]);
120 if (!$uid) {
121 return array(false, __("You must be logged in before you can edit package information."));
124 if (isset($_POST["comment_id"])) {
125 $comment_id = $_POST["comment_id"];
126 } else {
127 return array(false, __("Missing comment ID."));
130 if (!$unpin) {
131 if (pkgbase_comments_count($_POST['package_base'], false, true) >= 5){
132 return array(false, __("No more than 5 comments can be pinned."));
136 if (!can_pin_comment($comment_id)) {
137 if (!$unpin) {
138 return array(false, __("You are not allowed to pin this comment."));
139 } else {
140 return array(false, __("You are not allowed to unpin this comment."));
144 $dbh = DB::connect();
145 $q = "UPDATE PackageComments ";
146 if (!$unpin) {
147 $q.= "SET PinnedTS = UNIX_TIMESTAMP() ";
148 } else {
149 $q.= "SET PinnedTS = 0 ";
151 $q.= "WHERE ID = " . intval($comment_id);
152 $dbh->exec($q);
154 if (!$unpin) {
155 return array(true, __("Comment has been pinned."));
156 } else {
157 return array(true, __("Comment has been unpinned."));
163 * Get a list of all packages a logged-in user has voted for
165 * @param string $sid The session ID of the visitor
167 * @return array All packages the visitor has voted for
169 function pkgbase_votes_from_sid($sid="") {
170 $pkgs = array();
171 if (!$sid) {return $pkgs;}
172 $dbh = DB::connect();
173 $q = "SELECT PackageBaseID ";
174 $q.= "FROM PackageVotes, Users, Sessions ";
175 $q.= "WHERE Users.ID = Sessions.UsersID ";
176 $q.= "AND Users.ID = PackageVotes.UsersID ";
177 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
178 $result = $dbh->query($q);
179 if ($result) {
180 while ($row = $result->fetch(PDO::FETCH_NUM)) {
181 $pkgs[$row[0]] = 1;
184 return $pkgs;
188 * Get the package base details
190 * @param string $id The package base ID to get description for
192 * @return array The package base's details OR error message
194 function pkgbase_get_details($base_id) {
195 $dbh = DB::connect();
197 $q = "SELECT PackageBases.ID, PackageBases.Name, ";
198 $q.= "PackageBases.NumVotes, PackageBases.Popularity, ";
199 $q.= "PackageBases.OutOfDateTS, PackageBases.SubmittedTS, ";
200 $q.= "PackageBases.ModifiedTS, PackageBases.SubmitterUID, ";
201 $q.= "PackageBases.MaintainerUID, PackageBases.PackagerUID, ";
202 $q.= "PackageBases.FlaggerUID, ";
203 $q.= "(SELECT COUNT(*) FROM PackageRequests ";
204 $q.= " WHERE PackageRequests.PackageBaseID = PackageBases.ID ";
205 $q.= " AND PackageRequests.Status = 0) AS RequestCount ";
206 $q.= "FROM PackageBases ";
207 $q.= "WHERE PackageBases.ID = " . intval($base_id);
208 $result = $dbh->query($q);
210 $row = array();
212 if (!$result) {
213 $row['error'] = __("Error retrieving package details.");
215 else {
216 $row = $result->fetch(PDO::FETCH_ASSOC);
217 if (empty($row)) {
218 $row['error'] = __("Package details could not be found.");
222 return $row;
226 * Display the package base details page
228 * @param string $id The package base ID to get details page for
229 * @param array $row Package base details retrieved by pkgbase_get_details()
230 * @param string $SID The session ID of the visitor
232 * @return void
234 function pkgbase_display_details($base_id, $row, $SID="") {
235 $dbh = DB::connect();
237 if (isset($row['error'])) {
238 print "<p>" . $row['error'] . "</p>\n";
240 else {
241 $pkgbase_name = pkgbase_name_from_id($base_id);
243 include('pkgbase_details.php');
245 if ($SID) {
246 include('pkg_comment_box.php');
249 $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED);
251 $limit_pinned = isset($_GET['pinned']) ? 0 : 5;
252 $pinned = pkgbase_comments($base_id, $limit_pinned, false, true);
253 if (!empty($pinned)) {
254 include('pkg_comments.php');
255 unset($pinned);
258 $limit = isset($_GET['comments']) ? 0 : 10;
259 $comments = pkgbase_comments($base_id, $limit, $include_deleted);
260 if (!empty($comments)) {
261 include('pkg_comments.php');
267 * Convert a list of package IDs into a list of corresponding package bases.
269 * @param array|int $ids Array of package IDs to convert
271 * @return array|int List of package base IDs
273 function pkgbase_from_pkgid($ids) {
274 $dbh = DB::connect();
276 if (is_array($ids)) {
277 $q = "SELECT PackageBaseID FROM Packages ";
278 $q.= "WHERE ID IN (" . implode(",", $ids) . ")";
279 $result = $dbh->query($q);
280 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
281 } else {
282 $q = "SELECT PackageBaseID FROM Packages ";
283 $q.= "WHERE ID = " . $ids;
284 $result = $dbh->query($q);
285 return $result->fetch(PDO::FETCH_COLUMN, 0);
290 * Retrieve ID of a package base by name
292 * @param string $name The package base name to retrieve the ID for
294 * @return int The ID of the package base
296 function pkgbase_from_name($name) {
297 $dbh = DB::connect();
298 $q = "SELECT ID FROM PackageBases WHERE Name = " . $dbh->quote($name);
299 $result = $dbh->query($q);
300 return $result->fetch(PDO::FETCH_COLUMN, 0);
304 * Retrieve the name of a package base given its ID
306 * @param int $base_id The ID of the package base to query
308 * @return string The name of the package base
310 function pkgbase_name_from_id($base_id) {
311 $dbh = DB::connect();
312 $q = "SELECT Name FROM PackageBases WHERE ID = " . intval($base_id);
313 $result = $dbh->query($q);
314 return $result->fetch(PDO::FETCH_COLUMN, 0);
318 * Get the names of all packages belonging to a package base
320 * @param int $base_id The ID of the package base
322 * @return array The names of all packages belonging to the package base
324 function pkgbase_get_pkgnames($base_id) {
325 $dbh = DB::connect();
326 $q = "SELECT Name FROM Packages WHERE PackageBaseID = " . intval($base_id);
327 $result = $dbh->query($q);
328 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
332 * Delete all packages belonging to a package base
334 * @param int $base_id The ID of the package base
336 * @return void
338 function pkgbase_delete_packages($base_id) {
339 $dbh = DB::connect();
340 $q = "DELETE FROM Packages WHERE PackageBaseID = " . intval($base_id);
341 $dbh->exec($q);
345 * Retrieve the maintainer of a package base given its ID
347 * @param int $base_id The ID of the package base to query
349 * @return int The user ID of the current package maintainer
351 function pkgbase_maintainer_uid($base_id) {
352 $dbh = DB::connect();
353 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID = " . intval($base_id);
354 $result = $dbh->query($q);
355 return $result->fetch(PDO::FETCH_COLUMN, 0);
359 * Retrieve the maintainers of an array of package bases given by their ID
361 * @param int $base_ids The array of IDs of the package bases to query
363 * @return int The user ID of the current package maintainer
365 function pkgbase_maintainer_uids($base_ids) {
366 $dbh = DB::connect();
367 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID IN (" . implode(",", $base_ids) . ")";
368 $result = $dbh->query($q);
369 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
373 * Flag package(s) as out-of-date
375 * @param array $base_ids Array of package base IDs to flag/unflag
376 * @param string $comment The comment to add
378 * @return array Tuple of success/failure indicator and error message
380 function pkgbase_flag($base_ids, $comment) {
381 if (!has_credential(CRED_PKGBASE_FLAG)) {
382 return array(false, __("You must be logged in before you can flag packages."));
385 $base_ids = sanitize_ids($base_ids);
386 if (empty($base_ids)) {
387 return array(false, __("You did not select any packages to flag."));
390 if (strlen($comment) < 3) {
391 return array(false, __("The selected packages have not been flagged, please enter a comment."));
394 $uid = uid_from_sid($_COOKIE['AURSID']);
395 $dbh = DB::connect();
397 $q = "UPDATE PackageBases SET ";
398 $q.= "OutOfDateTS = UNIX_TIMESTAMP(), FlaggerUID = " . $uid . ", ";
399 $q.= "FlaggerComment = " . $dbh->quote($comment) . " ";
400 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
401 $q.= "AND OutOfDateTS IS NULL";
402 $dbh->exec($q);
404 foreach ($base_ids as $base_id) {
405 notify(array('flag', $uid, $base_id));
408 return array(true, __("The selected packages have been flagged out-of-date."));
412 * Unflag package(s) as out-of-date
414 * @param array $base_ids Array of package base IDs to flag/unflag
416 * @return array Tuple of success/failure indicator and error message
418 function pkgbase_unflag($base_ids) {
419 $uid = uid_from_sid($_COOKIE["AURSID"]);
420 if (!$uid) {
421 return array(false, __("You must be logged in before you can unflag packages."));
424 $base_ids = sanitize_ids($base_ids);
425 if (empty($base_ids)) {
426 return array(false, __("You did not select any packages to unflag."));
429 $dbh = DB::connect();
431 $q = "UPDATE PackageBases SET ";
432 $q.= "OutOfDateTS = NULL ";
433 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
435 $maintainers = array_merge(pkgbase_maintainer_uids($base_ids), pkgbase_get_comaintainer_uids($base_ids));
436 if (!has_credential(CRED_PKGBASE_UNFLAG, $maintainers)) {
437 $q.= "AND (MaintainerUID = " . $uid . " OR FlaggerUID = " . $uid. ")";
440 $result = $dbh->exec($q);
442 if ($result) {
443 return array(true, __("The selected packages have been unflagged."));
448 * Get package flag OOD comment
450 * @param int $base_id
452 * @return array Tuple of pkgbase ID, reason for OOD, and user who flagged
454 function pkgbase_get_flag_comment($base_id) {
455 $base_id = intval($base_id);
456 $dbh = DB::connect();
458 $q = "SELECT FlaggerComment,OutOfDateTS,Username FROM PackageBases ";
459 $q.= "LEFT JOIN Users ON FlaggerUID = Users.ID ";
460 $q.= "WHERE PackageBases.ID = " . $base_id . " ";
461 $q.= "AND PackageBases.OutOfDateTS IS NOT NULL";
462 $result = $dbh->query($q);
464 $row = array();
466 if (!$result) {
467 $row['error'] = __("Error retrieving package details.");
469 else {
470 $row = $result->fetch(PDO::FETCH_ASSOC);
471 if (empty($row)) {
472 $row['error'] = __("Package details could not be found.");
476 return $row;
480 * Delete package bases
482 * @param array $base_ids Array of package base IDs to delete
483 * @param int $merge_base_id Package base to merge the deleted ones into
484 * @param int $via Package request to close upon deletion
485 * @param bool $grant Allow anyone to delete the package base
487 * @return array Tuple of success/failure indicator and error message
489 function pkgbase_delete ($base_ids, $merge_base_id, $via, $grant=false) {
490 if (!$grant && !has_credential(CRED_PKGBASE_DELETE)) {
491 return array(false, __("You do not have permission to delete packages."));
494 $base_ids = sanitize_ids($base_ids);
495 if (empty($base_ids)) {
496 return array(false, __("You did not select any packages to delete."));
499 $dbh = DB::connect();
501 if ($merge_base_id) {
502 $merge_base_name = pkgbase_name_from_id($merge_base_id);
505 $uid = uid_from_sid($_COOKIE['AURSID']);
506 foreach ($base_ids as $base_id) {
507 if ($merge_base_id) {
508 notify(array('delete', $uid, $base_id, $merge_base_id));
509 } else {
510 notify(array('delete', $uid, $base_id));
515 * Close package request if the deletion was initiated through the
516 * request interface. NOTE: This needs to happen *before* the actual
517 * deletion. Otherwise, the former maintainer will not be included in
518 * the Cc list of the request notification email.
520 if ($via) {
521 pkgreq_close(intval($via), 'accepted', '');
524 /* Scan through pending deletion requests and close them. */
525 if (!$action) {
526 $username = username_from_sid($_COOKIE['AURSID']);
527 foreach ($base_ids as $base_id) {
528 $pkgreq_ids = array_merge(pkgreq_by_pkgbase($base_id));
529 foreach ($pkgreq_ids as $pkgreq_id) {
530 pkgreq_close(intval($pkgreq_id), 'accepted',
531 'The user ' . $username .
532 ' deleted the package.', true);
537 if ($merge_base_id) {
538 /* Merge comments */
539 $q = "UPDATE PackageComments ";
540 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
541 $q.= "WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
542 $dbh->exec($q);
544 /* Merge notifications */
545 $q = "SELECT DISTINCT UserID FROM CommentNotify cn ";
546 $q.= "WHERE PackageBaseID IN (" . implode(",", $base_ids) . ") ";
547 $q.= "AND NOT EXISTS (SELECT * FROM CommentNotify cn2 ";
548 $q.= "WHERE cn2.PackageBaseID = " . intval($merge_base_id) . " ";
549 $q.= "AND cn2.UserID = cn.UserID)";
550 $result = $dbh->query($q);
552 while ($notify_uid = $result->fetch(PDO::FETCH_COLUMN, 0)) {
553 $q = "INSERT INTO CommentNotify (UserID, PackageBaseID) ";
554 $q.= "VALUES (" . intval($notify_uid) . ", " . intval($merge_base_id) . ")";
555 $dbh->exec($q);
558 /* Merge votes */
559 foreach ($base_ids as $base_id) {
560 $q = "UPDATE PackageVotes ";
561 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
562 $q.= "WHERE PackageBaseID = " . $base_id . " ";
563 $q.= "AND UsersID NOT IN (";
564 $q.= "SELECT * FROM (SELECT UsersID ";
565 $q.= "FROM PackageVotes ";
566 $q.= "WHERE PackageBaseID = " . intval($merge_base_id);
567 $q.= ") temp)";
568 $dbh->exec($q);
571 $q = "UPDATE PackageBases ";
572 $q.= "SET NumVotes = (SELECT COUNT(*) FROM PackageVotes ";
573 $q.= "WHERE PackageBaseID = " . intval($merge_base_id) . ") ";
574 $q.= "WHERE ID = " . intval($merge_base_id);
575 $dbh->exec($q);
578 $q = "DELETE FROM Packages WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
579 $dbh->exec($q);
581 $q = "DELETE FROM PackageBases WHERE ID IN (" . implode(",", $base_ids) . ")";
582 $dbh->exec($q);
584 return array(true, __("The selected packages have been deleted."));
588 * Adopt or disown packages
590 * @param array $base_ids Array of package base IDs to adopt/disown
591 * @param bool $action Adopts if true, disowns if false. Adopts by default
592 * @param int $via Package request to close upon adoption
594 * @return array Tuple of success/failure indicator and error message
596 function pkgbase_adopt ($base_ids, $action=true, $via) {
597 $dbh = DB::connect();
599 $uid = uid_from_sid($_COOKIE["AURSID"]);
600 if (!$uid) {
601 if ($action) {
602 return array(false, __("You must be logged in before you can adopt packages."));
603 } else {
604 return array(false, __("You must be logged in before you can disown packages."));
608 /* Verify package ownership. */
609 $base_ids = sanitize_ids($base_ids);
611 $q = "SELECT ID FROM PackageBases ";
612 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
614 if ($action && !has_credential(CRED_PKGBASE_ADOPT)) {
615 /* Regular users may only adopt orphan packages. */
616 $q.= "AND MaintainerUID IS NULL";
618 if (!$action && !has_credential(CRED_PKGBASE_DISOWN)) {
619 /* Regular users may only disown their own packages. */
620 $q.= "AND MaintainerUID = " . $uid;
623 $result = $dbh->query($q);
624 $base_ids = $result->fetchAll(PDO::FETCH_COLUMN, 0);
626 /* Error out if the list of remaining packages is empty. */
627 if (empty($base_ids)) {
628 if ($action) {
629 return array(false, __("You did not select any packages to adopt."));
630 } else {
631 return array(false, __("You did not select any packages to disown."));
636 * Close package request if the disownment was initiated through the
637 * request interface. NOTE: This needs to happen *before* the actual
638 * disown operation. Otherwise, the former maintainer will not be
639 * included in the Cc list of the request notification email.
641 if ($via) {
642 pkgreq_close(intval($via), 'accepted', '');
645 /* Scan through pending orphan requests and close them. */
646 if (!$action) {
647 $username = username_from_sid($_COOKIE['AURSID']);
648 foreach ($base_ids as $base_id) {
649 $pkgreq_ids = pkgreq_by_pkgbase($base_id, 'orphan');
650 foreach ($pkgreq_ids as $pkgreq_id) {
651 pkgreq_close(intval($pkgreq_id), 'accepted',
652 'The user ' . $username .
653 ' disowned the package.', true);
658 /* Adopt or disown the package. */
659 if ($action) {
660 $q = "UPDATE PackageBases ";
661 $q.= "SET MaintainerUID = $uid ";
662 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
663 $dbh->exec($q);
664 } else {
665 /* Update the co-maintainer list when disowning a package. */
666 if (has_credential(CRED_PKGBASE_DISOWN)) {
667 foreach ($base_ids as $base_id) {
668 pkgbase_set_comaintainers($base_id, array());
671 $q = "UPDATE PackageBases ";
672 $q.= "SET MaintainerUID = NULL ";
673 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
674 $dbh->exec($q);
675 } else {
676 foreach ($base_ids as $base_id) {
677 $comaintainers = pkgbase_get_comaintainers($base_id);
679 if (count($comaintainers) > 0) {
680 $uid = uid_from_username($comaintainers[0]);
681 $comaintainers = array_diff($comaintainers, array($comaintainers[0]));
682 pkgbase_set_comaintainers($base_id, $comaintainers);
683 } else {
684 $uid = "NULL";
687 $q = "UPDATE PackageBases ";
688 $q.= "SET MaintainerUID = " . $uid . " ";
689 $q.= "WHERE ID = " . $base_id;
690 $dbh->exec($q);
695 if ($action) {
696 pkgbase_notify($base_ids);
697 return array(true, __("The selected packages have been adopted."));
698 } else {
699 return array(true, __("The selected packages have been disowned."));
704 * Vote and un-vote for packages
706 * @param array $base_ids Array of package base IDs to vote/un-vote
707 * @param bool $action Votes if true, un-votes if false. Votes by default
709 * @return array Tuple of success/failure indicator and error message
711 function pkgbase_vote ($base_ids, $action=true) {
712 if (!has_credential(CRED_PKGBASE_VOTE)) {
713 if ($action) {
714 return array(false, __("You must be logged in before you can vote for packages."));
715 } else {
716 return array(false, __("You must be logged in before you can un-vote for packages."));
720 $base_ids = sanitize_ids($base_ids);
721 if (empty($base_ids)) {
722 if ($action) {
723 return array(false, __("You did not select any packages to vote for."));
724 } else {
725 return array(false, __("Your votes have been removed from the selected packages."));
729 $dbh = DB::connect();
730 $my_votes = pkgbase_votes_from_sid($_COOKIE["AURSID"]);
731 $uid = uid_from_sid($_COOKIE["AURSID"]);
733 $first = 1;
734 foreach ($base_ids as $pid) {
735 if ($action) {
736 $check = !isset($my_votes[$pid]);
737 } else {
738 $check = isset($my_votes[$pid]);
741 if ($check) {
742 if ($first) {
743 $first = 0;
744 $vote_ids = $pid;
745 if ($action) {
746 $vote_clauses = "($uid, $pid, UNIX_TIMESTAMP())";
748 } else {
749 $vote_ids .= ", $pid";
750 if ($action) {
751 $vote_clauses .= ", ($uid, $pid, UNIX_TIMESTAMP())";
757 /* Only add votes for packages the user hasn't already voted for. */
758 $op = $action ? "+" : "-";
759 $q = "UPDATE PackageBases SET NumVotes = NumVotes $op 1 ";
760 $q.= "WHERE ID IN ($vote_ids)";
762 $dbh->exec($q);
764 if ($action) {
765 $q = "INSERT INTO PackageVotes (UsersID, PackageBaseID, VoteTS) VALUES ";
766 $q.= $vote_clauses;
767 } else {
768 $q = "DELETE FROM PackageVotes WHERE UsersID = $uid ";
769 $q.= "AND PackageBaseID IN ($vote_ids)";
772 $dbh->exec($q);
774 if ($action) {
775 return array(true, __("Your votes have been cast for the selected packages."));
776 } else {
777 return array(true, __("Your votes have been removed from the selected packages."));
782 * Get all usernames and IDs that voted for a specific package base
784 * @param string $pkgbase_name The package base to retrieve votes for
786 * @return array User IDs and usernames that voted for a specific package base
788 function pkgbase_votes_from_name($pkgbase_name) {
789 $dbh = DB::connect();
791 $q = "SELECT UsersID, Username, Name, VoteTS FROM PackageVotes ";
792 $q.= "LEFT JOIN Users ON UsersID = Users.ID ";
793 $q.= "LEFT JOIN PackageBases ";
794 $q.= "ON PackageVotes.PackageBaseID = PackageBases.ID ";
795 $q.= "WHERE PackageBases.Name = ". $dbh->quote($pkgbase_name) . " ";
796 $q.= "ORDER BY Username";
797 $result = $dbh->query($q);
799 if (!$result) {
800 return;
803 $votes = array();
804 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
805 $votes[] = $row;
808 return $votes;
812 * Determine if a user has already voted for a specific package base
814 * @param string $uid The user ID to check for an existing vote
815 * @param string $base_id The package base ID to check for an existing vote
817 * @return bool True if the user has already voted, otherwise false
819 function pkgbase_user_voted($uid, $base_id) {
820 $dbh = DB::connect();
821 $q = "SELECT COUNT(*) FROM PackageVotes WHERE ";
822 $q.= "UsersID = ". $dbh->quote($uid) . " AND ";
823 $q.= "PackageBaseID = " . $dbh->quote($base_id);
824 $result = $dbh->query($q);
825 if (!$result) {
826 return null;
829 return ($result->fetch(PDO::FETCH_COLUMN, 0) > 0);
833 * Determine if a user wants notifications for a specific package base
835 * @param string $uid User ID to check in the database
836 * @param string $base_id Package base ID to check notifications for
838 * @return bool True if the user wants notifications, otherwise false
840 function pkgbase_user_notify($uid, $base_id) {
841 $dbh = DB::connect();
843 $q = "SELECT * FROM CommentNotify WHERE UserID = " . $dbh->quote($uid);
844 $q.= " AND PackageBaseID = " . $dbh->quote($base_id);
845 $result = $dbh->query($q);
847 if ($result->fetch(PDO::FETCH_NUM)) {
848 return true;
850 else {
851 return false;
856 * Toggle notification of packages
858 * @param array $base_ids Array of package base IDs to toggle
860 * @return array Tuple of success/failure indicator and error message
862 function pkgbase_notify ($base_ids, $action=true) {
863 if (!has_credential(CRED_PKGBASE_NOTIFY)) {
864 return;
867 $base_ids = sanitize_ids($base_ids);
868 if (empty($base_ids)) {
869 return array(false, __("Couldn't add to notification list."));
872 $dbh = DB::connect();
873 $uid = uid_from_sid($_COOKIE["AURSID"]);
875 $output = "";
877 $first = true;
880 * There currently shouldn't be multiple requests here, but the format
881 * in which it's sent requires this.
883 foreach ($base_ids as $bid) {
884 $q = "SELECT Name FROM PackageBases WHERE ID = $bid";
885 $result = $dbh->query($q);
886 if ($result) {
887 $row = $result->fetch(PDO::FETCH_NUM);
888 $basename = $row[0];
890 else {
891 $basename = '';
894 if ($first)
895 $first = false;
896 else
897 $output .= ", ";
900 if ($action) {
901 $q = "SELECT COUNT(*) FROM CommentNotify WHERE ";
902 $q .= "UserID = $uid AND PackageBaseID = $bid";
904 /* Notification already added. Don't add again. */
905 $result = $dbh->query($q);
906 if ($result->fetchColumn() == 0) {
907 $q = "INSERT INTO CommentNotify (PackageBaseID, UserID) VALUES ($bid, $uid)";
908 $dbh->exec($q);
911 $output .= $basename;
913 else {
914 $q = "DELETE FROM CommentNotify WHERE PackageBaseID = $bid ";
915 $q .= "AND UserID = $uid";
916 $dbh->exec($q);
918 $output .= $basename;
922 if ($action) {
923 $output = __("You have been added to the comment notification list for %s.", $output);
925 else {
926 $output = __("You have been removed from the comment notification list for %s.", $output);
929 return array(true, $output);
933 * Delete a package comment
935 * @return array Tuple of success/failure indicator and error message
937 function pkgbase_delete_comment() {
938 $uid = uid_from_sid($_COOKIE["AURSID"]);
939 if (!$uid) {
940 return array(false, __("You must be logged in before you can edit package information."));
943 if (isset($_POST["comment_id"])) {
944 $comment_id = $_POST["comment_id"];
945 } else {
946 return array(false, __("Missing comment ID."));
949 $dbh = DB::connect();
950 if (can_delete_comment($comment_id)) {
951 $q = "UPDATE PackageComments ";
952 $q.= "SET DelUsersID = ".$uid.", ";
953 $q.= "DelTS = UNIX_TIMESTAMP() ";
954 $q.= "WHERE ID = ".intval($comment_id);
955 $dbh->exec($q);
956 return array(true, __("Comment has been deleted."));
957 } else {
958 return array(false, __("You are not allowed to delete this comment."));
963 * Edit a package comment
965 * @return array Tuple of success/failure indicator and error message
967 function pkgbase_edit_comment($comment) {
968 $uid = uid_from_sid($_COOKIE["AURSID"]);
969 if (!$uid) {
970 return array(false, __("You must be logged in before you can edit package information."));
973 if (isset($_POST["comment_id"])) {
974 $comment_id = $_POST["comment_id"];
975 } else {
976 return array(false, __("Missing comment ID."));
979 if (trim($comment) == '') {
980 return array(false, __('Comment cannot be empty.'));
983 $dbh = DB::connect();
984 if (can_edit_comment($comment_id)) {
985 $q = "UPDATE PackageComments ";
986 $q.= "SET EditedUsersID = ".$uid.", ";
987 $q.= "Comments = ".$dbh->quote($comment).", ";
988 $q.= "EditedTS = UNIX_TIMESTAMP() ";
989 $q.= "WHERE ID = ".intval($comment_id);
990 $dbh->exec($q);
991 return array(true, __("Comment has been edited."));
992 } else {
993 return array(false, __("You are not allowed to edit this comment."));
998 * Get a list of package base keywords
1000 * @param int $base_id The package base ID to retrieve the keywords for
1002 * @return array An array of keywords
1004 function pkgbase_get_keywords($base_id) {
1005 $dbh = DB::connect();
1006 $q = "SELECT Keyword FROM PackageKeywords ";
1007 $q .= "WHERE PackageBaseID = " . intval($base_id) . " ";
1008 $q .= "ORDER BY Keyword ASC";
1009 $result = $dbh->query($q);
1011 if ($result) {
1012 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1013 } else {
1014 return array();
1019 * Update the list of keywords of a package base
1021 * @param int $base_id The package base ID to update the keywords of
1022 * @param array $users Array of keywords
1024 * @return array Tuple of success/failure indicator and error message
1026 function pkgbase_set_keywords($base_id, $keywords) {
1027 $base_id = intval($base_id);
1029 $maintainers = array_merge(array(pkgbase_maintainer_uid($base_id)), pkgbase_get_comaintainer_uids(array($base_id)));
1030 if (!has_credential(CRED_PKGBASE_SET_KEYWORDS, $maintainers)) {
1031 return array(false, __("You are not allowed to edit the keywords of this package base."));
1034 /* Remove empty and duplicate user names. */
1035 $keywords = array_unique(array_filter(array_map('trim', $keywords)));
1037 $dbh = DB::connect();
1039 $q = sprintf("DELETE FROM PackageKeywords WHERE PackageBaseID = %d", $base_id);
1040 $dbh->exec($q);
1042 $i = 0;
1043 foreach ($keywords as $keyword) {
1044 $q = sprintf("INSERT INTO PackageKeywords (PackageBaseID, Keyword) VALUES (%d, %s)", $base_id, $dbh->quote($keyword));
1045 var_dump($q);
1046 $dbh->exec($q);
1048 $i++;
1049 if ($i >= 20) {
1050 break;
1054 return array(true, __("The package base keywords have been updated."));
1058 * Get a list of package base co-maintainers
1060 * @param int $base_id The package base ID to retrieve the co-maintainers for
1062 * @return array An array of co-maintainer user names
1064 function pkgbase_get_comaintainers($base_id) {
1065 $dbh = DB::connect();
1066 $q = "SELECT UserName FROM PackageComaintainers ";
1067 $q .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
1068 $q .= "WHERE PackageComaintainers.PackageBaseID = " . intval($base_id) . " ";
1069 $q .= "ORDER BY Priority ASC";
1070 $result = $dbh->query($q);
1072 if ($result) {
1073 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1074 } else {
1075 return array();
1080 * Get a list of package base co-maintainer IDs
1082 * @param int $base_id The package base ID to retrieve the co-maintainers for
1084 * @return array An array of co-maintainer user UDs
1086 function pkgbase_get_comaintainer_uids($base_ids) {
1087 $dbh = DB::connect();
1088 $q = "SELECT UsersID FROM PackageComaintainers ";
1089 $q .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
1090 $q .= "WHERE PackageComaintainers.PackageBaseID IN (" . implode(",", $base_ids) . ") ";
1091 $q .= "ORDER BY Priority ASC";
1092 $result = $dbh->query($q);
1094 if ($result) {
1095 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1096 } else {
1097 return array();
1102 * Update the list of co-maintainers of a package base
1104 * @param int $base_id The package base ID to update the co-maintainers of
1105 * @param array $users Array of co-maintainer user names
1107 * @return array Tuple of success/failure indicator and error message
1109 function pkgbase_set_comaintainers($base_id, $users) {
1110 if (!has_credential(CRED_PKGBASE_EDIT_COMAINTAINERS, array(pkgbase_maintainer_uid($base_id)))) {
1111 return array(false, __("You are not allowed to manage co-maintainers of this package base."));
1114 /* Remove empty and duplicate user names. */
1115 $users = array_unique(array_filter(array_map('trim', $users)));
1117 $dbh = DB::connect();
1119 $uids_new = array();
1120 foreach($users as $user) {
1121 $q = "SELECT ID FROM Users ";
1122 $q .= "WHERE UserName = " . $dbh->quote($user);
1123 $result = $dbh->query($q);
1124 $uid = $result->fetchColumn(0);
1126 if (!$uid) {
1127 return array(false, __("Invalid user name: %s", $user));
1130 $uids_new[] = $uid;
1133 $q = sprintf("SELECT UsersID FROM PackageComaintainers WHERE PackageBaseID = %d", $base_id);
1134 $result = $dbh->query($q);
1135 $uids_old = $result->fetchAll(PDO::FETCH_COLUMN, 0);
1137 $uids_add = array_diff($uids_new, $uids_old);
1138 $uids_rem = array_diff($uids_old, $uids_new);
1140 $i = 1;
1141 foreach ($uids_new as $uid) {
1142 if (in_array($uid, $uids_add)) {
1143 $q = sprintf("INSERT INTO PackageComaintainers (PackageBaseID, UsersID, Priority) VALUES (%d, %d, %d)", $base_id, $uid, $i);
1144 notify(array('comaintainer-add', $base_id, $uid));
1145 } else {
1146 $q = sprintf("UPDATE PackageComaintainers SET Priority = %d WHERE PackageBaseID = %d AND UsersID = %d", $i, $base_id, $uid);
1149 $dbh->exec($q);
1150 $i++;
1153 foreach ($uids_rem as $uid) {
1154 $q = sprintf("DELETE FROM PackageComaintainers WHERE PackageBaseID = %d AND UsersID = %d", $base_id, $uid);
1155 $dbh->exec($q);
1156 notify(array('comaintainer-remove', $base_id, $uid));
1159 return array(true, __("The package base co-maintainers have been updated."));