Handle empty resultset getting recent 10 packages
[aur.git] / web / lib / pkgbasefuncs.inc.php
blob72c33b6d3e568a84d90eadbb5a3213ecc032e7cc
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, RenderedComment, ";
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();
83 * Invoke the comment rendering script.
85 * @param int $id ID of the comment to render
87 * @return void
89 function render_comment($id) {
90 $cmd = config_get('options', 'render-comment-cmd');
91 $cmd .= ' ' . intval($id);
93 $descspec = array(
94 0 => array('pipe', 'r'),
95 1 => array('pipe', 'w'),
96 2 => array('pipe', 'w')
99 $p = proc_open($cmd, $descspec, $pipes);
101 if (!is_resource($p)) {
102 return false;
105 fclose($pipes[0]);
106 fclose($pipes[1]);
107 fclose($pipes[2]);
109 return proc_close($p);
113 * Add a comment to a package page and send out appropriate notifications
115 * @param string $base_id The package base ID to add the comment on
116 * @param string $uid The user ID of the individual who left the comment
117 * @param string $comment The comment left on a package page
119 * @return void
121 function pkgbase_add_comment($base_id, $uid, $comment) {
122 $dbh = DB::connect();
124 if (trim($comment) == '') {
125 return array(false, __('Comment cannot be empty.'));
128 $q = "INSERT INTO PackageComments ";
129 $q.= "(PackageBaseID, UsersID, Comments, RenderedComment, CommentTS) ";
130 $q.= "VALUES (" . intval($base_id) . ", " . $uid . ", ";
131 $q.= $dbh->quote($comment) . ", '', " . strval(time()) . ")";
132 $dbh->exec($q);
133 $comment_id = $dbh->lastInsertId();
135 render_comment($comment_id);
137 notify(array('comment', $uid, $base_id, $comment_id));
139 return array(true, __('Comment has been added.'));
143 * Pin/unpin a package comment
145 * @param bool $unpin True if unpinning rather than pinning
147 * @return array Tuple of success/failure indicator and error message
149 function pkgbase_pin_comment($unpin=false) {
150 $uid = uid_from_sid($_COOKIE["AURSID"]);
152 if (!$uid) {
153 return array(false, __("You must be logged in before you can edit package information."));
156 if (isset($_POST["comment_id"])) {
157 $comment_id = $_POST["comment_id"];
158 } else {
159 return array(false, __("Missing comment ID."));
162 if (!$unpin) {
163 if (pkgbase_comments_count($_POST['package_base'], false, true) >= 5){
164 return array(false, __("No more than 5 comments can be pinned."));
168 if (!can_pin_comment($comment_id)) {
169 if (!$unpin) {
170 return array(false, __("You are not allowed to pin this comment."));
171 } else {
172 return array(false, __("You are not allowed to unpin this comment."));
176 $dbh = DB::connect();
177 $q = "UPDATE PackageComments ";
178 if (!$unpin) {
179 $q.= "SET PinnedTS = " . strval(time()) . " ";
180 } else {
181 $q.= "SET PinnedTS = 0 ";
183 $q.= "WHERE ID = " . intval($comment_id);
184 $dbh->exec($q);
186 if (!$unpin) {
187 return array(true, __("Comment has been pinned."));
188 } else {
189 return array(true, __("Comment has been unpinned."));
195 * Get a list of all packages a logged-in user has voted for
197 * @param string $sid The session ID of the visitor
199 * @return array All packages the visitor has voted for
201 function pkgbase_votes_from_sid($sid="") {
202 $pkgs = array();
203 if (!$sid) {return $pkgs;}
204 $dbh = DB::connect();
205 $q = "SELECT PackageBaseID ";
206 $q.= "FROM PackageVotes, Users, Sessions ";
207 $q.= "WHERE Users.ID = Sessions.UsersID ";
208 $q.= "AND Users.ID = PackageVotes.UsersID ";
209 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
210 $result = $dbh->query($q);
211 if ($result) {
212 while ($row = $result->fetch(PDO::FETCH_NUM)) {
213 $pkgs[$row[0]] = 1;
216 return $pkgs;
220 * Get the package base details
222 * @param string $id The package base ID to get description for
224 * @return array The package base's details OR error message
226 function pkgbase_get_details($base_id) {
227 $dbh = DB::connect();
229 $q = "SELECT PackageBases.ID, PackageBases.Name, ";
230 $q.= "PackageBases.NumVotes, PackageBases.Popularity, ";
231 $q.= "PackageBases.OutOfDateTS, PackageBases.SubmittedTS, ";
232 $q.= "PackageBases.ModifiedTS, PackageBases.SubmitterUID, ";
233 $q.= "PackageBases.MaintainerUID, PackageBases.PackagerUID, ";
234 $q.= "PackageBases.FlaggerUID, ";
235 $q.= "(SELECT COUNT(*) FROM PackageRequests ";
236 $q.= " WHERE PackageRequests.PackageBaseID = PackageBases.ID ";
237 $q.= " AND PackageRequests.Status = 0) AS RequestCount ";
238 $q.= "FROM PackageBases ";
239 $q.= "WHERE PackageBases.ID = " . intval($base_id);
240 $result = $dbh->query($q);
242 $row = array();
244 if (!$result) {
245 $row['error'] = __("Error retrieving package details.");
247 else {
248 $row = $result->fetch(PDO::FETCH_ASSOC);
249 if (empty($row)) {
250 $row['error'] = __("Package details could not be found.");
254 return $row;
258 * Display the package base details page
260 * @param string $id The package base ID to get details page for
261 * @param array $row Package base details retrieved by pkgbase_get_details()
262 * @param string $SID The session ID of the visitor
264 * @return void
266 function pkgbase_display_details($base_id, $row, $SID="") {
267 if (isset($row['error'])) {
268 print "<p>" . $row['error'] . "</p>\n";
270 else {
271 $pkgbase_name = pkgbase_name_from_id($base_id);
273 include('pkgbase_details.php');
275 if ($SID) {
276 include('pkg_comment_box.php');
279 $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED);
281 $limit_pinned = isset($_GET['pinned']) ? 0 : 5;
282 $pinned = pkgbase_comments($base_id, $limit_pinned, false, true);
283 if (!empty($pinned)) {
284 include('pkg_comments.php');
286 unset($pinned);
288 $limit = isset($_GET['comments']) ? 0 : 10;
289 $comments = pkgbase_comments($base_id, $limit, $include_deleted);
290 if (!empty($comments)) {
291 include('pkg_comments.php');
297 * Convert a list of package IDs into a list of corresponding package bases.
299 * @param array|int $ids Array of package IDs to convert
301 * @return array|int List of package base IDs
303 function pkgbase_from_pkgid($ids) {
304 $dbh = DB::connect();
306 if (is_array($ids)) {
307 $q = "SELECT PackageBaseID FROM Packages ";
308 $q.= "WHERE ID IN (" . implode(",", $ids) . ")";
309 $result = $dbh->query($q);
310 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
311 } else {
312 $q = "SELECT PackageBaseID FROM Packages ";
313 $q.= "WHERE ID = " . $ids;
314 $result = $dbh->query($q);
315 return $result->fetch(PDO::FETCH_COLUMN, 0);
320 * Retrieve ID of a package base by name
322 * @param string $name The package base name to retrieve the ID for
324 * @return int The ID of the package base
326 function pkgbase_from_name($name) {
327 $dbh = DB::connect();
328 $q = "SELECT ID FROM PackageBases WHERE Name = " . $dbh->quote($name);
329 $result = $dbh->query($q);
330 return $result->fetch(PDO::FETCH_COLUMN, 0);
334 * Retrieve the name of a package base given its ID
336 * @param int $base_id The ID of the package base to query
338 * @return string The name of the package base
340 function pkgbase_name_from_id($base_id) {
341 $dbh = DB::connect();
342 $q = "SELECT Name FROM PackageBases WHERE ID = " . intval($base_id);
343 $result = $dbh->query($q);
344 return $result->fetch(PDO::FETCH_COLUMN, 0);
348 * Get the names of all packages belonging to a package base
350 * @param int $base_id The ID of the package base
352 * @return array The names of all packages belonging to the package base
354 function pkgbase_get_pkgnames($base_id) {
355 $dbh = DB::connect();
356 $q = "SELECT Name FROM Packages WHERE PackageBaseID = " . intval($base_id);
357 $result = $dbh->query($q);
358 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
362 * Delete all packages belonging to a package base
364 * @param int $base_id The ID of the package base
366 * @return void
368 function pkgbase_delete_packages($base_id) {
369 $dbh = DB::connect();
370 $q = "DELETE FROM Packages WHERE PackageBaseID = " . intval($base_id);
371 $dbh->exec($q);
375 * Retrieve the maintainer of a package base given its ID
377 * @param int $base_id The ID of the package base to query
379 * @return int The user ID of the current package maintainer
381 function pkgbase_maintainer_uid($base_id) {
382 $dbh = DB::connect();
383 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID = " . intval($base_id);
384 $result = $dbh->query($q);
385 return $result->fetch(PDO::FETCH_COLUMN, 0);
389 * Retrieve the maintainers of an array of package bases given by their ID
391 * @param int $base_ids The array of IDs of the package bases to query
393 * @return int The user ID of the current package maintainer
395 function pkgbase_maintainer_uids($base_ids) {
396 $dbh = DB::connect();
397 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID IN (" . implode(",", $base_ids) . ")";
398 $result = $dbh->query($q);
399 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
403 * Flag package(s) as out-of-date
405 * @param array $base_ids Array of package base IDs to flag/unflag
406 * @param string $comment The comment to add
408 * @return array Tuple of success/failure indicator and error message
410 function pkgbase_flag($base_ids, $comment) {
411 if (!has_credential(CRED_PKGBASE_FLAG)) {
412 return array(false, __("You must be logged in before you can flag packages."));
415 $base_ids = sanitize_ids($base_ids);
416 if (empty($base_ids)) {
417 return array(false, __("You did not select any packages to flag."));
420 if (strlen($comment) < 3) {
421 return array(false, __("The selected packages have not been flagged, please enter a comment."));
424 $uid = uid_from_sid($_COOKIE['AURSID']);
425 $dbh = DB::connect();
427 $q = "UPDATE PackageBases SET ";
428 $q.= "OutOfDateTS = " . strval(time()) . ", FlaggerUID = " . $uid . ", ";
429 $q.= "FlaggerComment = " . $dbh->quote($comment) . " ";
430 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
431 $q.= "AND OutOfDateTS IS NULL";
432 $dbh->exec($q);
434 foreach ($base_ids as $base_id) {
435 notify(array('flag', $uid, $base_id));
438 return array(true, __("The selected packages have been flagged out-of-date."));
442 * Unflag package(s) as out-of-date
444 * @param array $base_ids Array of package base IDs to flag/unflag
446 * @return array Tuple of success/failure indicator and error message
448 function pkgbase_unflag($base_ids) {
449 $uid = uid_from_sid($_COOKIE["AURSID"]);
450 if (!$uid) {
451 return array(false, __("You must be logged in before you can unflag packages."));
454 $base_ids = sanitize_ids($base_ids);
455 if (empty($base_ids)) {
456 return array(false, __("You did not select any packages to unflag."));
459 $dbh = DB::connect();
461 $q = "UPDATE PackageBases SET ";
462 $q.= "OutOfDateTS = NULL ";
463 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
465 $maintainers = array_merge(pkgbase_maintainer_uids($base_ids), pkgbase_get_comaintainer_uids($base_ids));
466 if (!has_credential(CRED_PKGBASE_UNFLAG, $maintainers)) {
467 $q.= "AND (MaintainerUID = " . $uid . " OR FlaggerUID = " . $uid. ")";
470 $result = $dbh->exec($q);
472 if ($result) {
473 return array(true, __("The selected packages have been unflagged."));
478 * Get package flag OOD comment
480 * @param int $base_id
482 * @return array Tuple of pkgbase ID, reason for OOD, and user who flagged
484 function pkgbase_get_flag_comment($base_id) {
485 $base_id = intval($base_id);
486 $dbh = DB::connect();
488 $q = "SELECT FlaggerComment,OutOfDateTS,Username FROM PackageBases ";
489 $q.= "LEFT JOIN Users ON FlaggerUID = Users.ID ";
490 $q.= "WHERE PackageBases.ID = " . $base_id . " ";
491 $q.= "AND PackageBases.OutOfDateTS IS NOT NULL";
492 $result = $dbh->query($q);
494 $row = array();
496 if (!$result) {
497 $row['error'] = __("Error retrieving package details.");
499 else {
500 $row = $result->fetch(PDO::FETCH_ASSOC);
501 if (empty($row)) {
502 $row['error'] = __("Package details could not be found.");
506 return $row;
510 * Delete package bases
512 * @param array $base_ids Array of package base IDs to delete
513 * @param int $merge_base_id Package base to merge the deleted ones into
514 * @param int $via Package request to close upon deletion
515 * @param bool $grant Allow anyone to delete the package base
517 * @return array Tuple of success/failure indicator and error message
519 function pkgbase_delete ($base_ids, $merge_base_id, $via, $grant=false) {
520 if (!$grant && !has_credential(CRED_PKGBASE_DELETE)) {
521 return array(false, __("You do not have permission to delete packages."));
524 $base_ids = sanitize_ids($base_ids);
525 if (empty($base_ids)) {
526 return array(false, __("You did not select any packages to delete."));
529 $dbh = DB::connect();
531 if ($merge_base_id) {
532 $merge_base_name = pkgbase_name_from_id($merge_base_id);
535 $uid = uid_from_sid($_COOKIE['AURSID']);
536 foreach ($base_ids as $base_id) {
537 if ($merge_base_id) {
538 notify(array('delete', $uid, $base_id, $merge_base_id));
539 } else {
540 notify(array('delete', $uid, $base_id));
545 * Close package request if the deletion was initiated through the
546 * request interface. NOTE: This needs to happen *before* the actual
547 * deletion. Otherwise, the former maintainer will not be included in
548 * the Cc list of the request notification email.
550 if ($via) {
551 pkgreq_close(intval($via), 'accepted', '');
554 /* Scan through pending deletion requests and close them. */
555 $username = username_from_sid($_COOKIE['AURSID']);
556 foreach ($base_ids as $base_id) {
557 $pkgreq_ids = array_merge(pkgreq_by_pkgbase($base_id));
558 foreach ($pkgreq_ids as $pkgreq_id) {
559 pkgreq_close(intval($pkgreq_id), 'accepted',
560 'The user ' . $username .
561 ' deleted the package.', true);
565 if ($merge_base_id) {
566 /* Merge comments */
567 $q = "UPDATE PackageComments ";
568 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
569 $q.= "WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
570 $dbh->exec($q);
572 /* Merge notifications */
573 $q = "SELECT DISTINCT UserID FROM PackageNotifications cn ";
574 $q.= "WHERE PackageBaseID IN (" . implode(",", $base_ids) . ") ";
575 $q.= "AND NOT EXISTS (SELECT * FROM PackageNotifications cn2 ";
576 $q.= "WHERE cn2.PackageBaseID = " . intval($merge_base_id) . " ";
577 $q.= "AND cn2.UserID = cn.UserID)";
578 $result = $dbh->query($q);
580 while ($notify_uid = $result->fetch(PDO::FETCH_COLUMN, 0)) {
581 $q = "INSERT INTO PackageNotifications (UserID, PackageBaseID) ";
582 $q.= "VALUES (" . intval($notify_uid) . ", " . intval($merge_base_id) . ")";
583 $dbh->exec($q);
586 /* Merge votes */
587 foreach ($base_ids as $base_id) {
588 $q = "UPDATE PackageVotes ";
589 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
590 $q.= "WHERE PackageBaseID = " . $base_id . " ";
591 $q.= "AND UsersID NOT IN (";
592 $q.= "SELECT * FROM (SELECT UsersID ";
593 $q.= "FROM PackageVotes ";
594 $q.= "WHERE PackageBaseID = " . intval($merge_base_id);
595 $q.= ") temp)";
596 $dbh->exec($q);
599 $q = "UPDATE PackageBases ";
600 $q.= "SET NumVotes = (SELECT COUNT(*) FROM PackageVotes ";
601 $q.= "WHERE PackageBaseID = " . intval($merge_base_id) . ") ";
602 $q.= "WHERE ID = " . intval($merge_base_id);
603 $dbh->exec($q);
606 $q = "DELETE FROM Packages WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
607 $dbh->exec($q);
609 $q = "DELETE FROM PackageBases WHERE ID IN (" . implode(",", $base_ids) . ")";
610 $dbh->exec($q);
612 return array(true, __("The selected packages have been deleted."));
616 * Adopt or disown packages
618 * @param array $base_ids Array of package base IDs to adopt/disown
619 * @param bool $action Adopts if true, disowns if false. Adopts by default
620 * @param int $via Package request to close upon adoption
622 * @return array Tuple of success/failure indicator and error message
624 function pkgbase_adopt ($base_ids, $action=true, $via) {
625 $dbh = DB::connect();
627 $uid = uid_from_sid($_COOKIE["AURSID"]);
628 if (!$uid) {
629 if ($action) {
630 return array(false, __("You must be logged in before you can adopt packages."));
631 } else {
632 return array(false, __("You must be logged in before you can disown packages."));
636 /* Verify package ownership. */
637 $base_ids = sanitize_ids($base_ids);
639 $q = "SELECT ID FROM PackageBases ";
640 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
642 if ($action && !has_credential(CRED_PKGBASE_ADOPT)) {
643 /* Regular users may only adopt orphan packages. */
644 $q.= "AND MaintainerUID IS NULL";
646 if (!$action && !has_credential(CRED_PKGBASE_DISOWN)) {
647 /* Regular users may only disown their own packages. */
648 $q.= "AND MaintainerUID = " . $uid;
651 $result = $dbh->query($q);
652 $base_ids = $result->fetchAll(PDO::FETCH_COLUMN, 0);
654 /* Error out if the list of remaining packages is empty. */
655 if (empty($base_ids)) {
656 if ($action) {
657 return array(false, __("You did not select any packages to adopt."));
658 } else {
659 return array(false, __("You did not select any packages to disown."));
664 * Close package request if the disownment was initiated through the
665 * request interface. NOTE: This needs to happen *before* the actual
666 * disown operation. Otherwise, the former maintainer will not be
667 * included in the Cc list of the request notification email.
669 if ($via) {
670 pkgreq_close(intval($via), 'accepted', '');
673 /* Scan through pending orphan requests and close them. */
674 if (!$action) {
675 $username = username_from_sid($_COOKIE['AURSID']);
676 foreach ($base_ids as $base_id) {
677 $pkgreq_ids = pkgreq_by_pkgbase($base_id, 'orphan');
678 foreach ($pkgreq_ids as $pkgreq_id) {
679 pkgreq_close(intval($pkgreq_id), 'accepted',
680 'The user ' . $username .
681 ' disowned the package.', true);
686 /* Adopt or disown the package. */
687 if ($action) {
688 $q = "UPDATE PackageBases ";
689 $q.= "SET MaintainerUID = $uid ";
690 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
691 $dbh->exec($q);
693 /* Add the new maintainer to the notification list. */
694 pkgbase_notify($base_ids);
695 } else {
696 /* Update the co-maintainer list when disowning a package. */
697 if (has_credential(CRED_PKGBASE_DISOWN)) {
698 foreach ($base_ids as $base_id) {
699 pkgbase_set_comaintainers($base_id, array());
702 $q = "UPDATE PackageBases ";
703 $q.= "SET MaintainerUID = NULL ";
704 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
705 $dbh->exec($q);
706 } else {
707 foreach ($base_ids as $base_id) {
708 $comaintainers = pkgbase_get_comaintainers($base_id);
710 if (count($comaintainers) > 0) {
711 $comaintainer_uid = uid_from_username($comaintainers[0]);
712 $comaintainers = array_diff($comaintainers, array($comaintainers[0]));
713 pkgbase_set_comaintainers($base_id, $comaintainers);
714 } else {
715 $comaintainer_uid = "NULL";
718 $q = "UPDATE PackageBases ";
719 $q.= "SET MaintainerUID = " . $comaintainer_uid . " ";
720 $q.= "WHERE ID = " . $base_id;
721 $dbh->exec($q);
726 foreach ($base_ids as $base_id) {
727 notify(array($action ? 'adopt' : 'disown', $base_id, $uid));
730 if ($action) {
731 return array(true, __("The selected packages have been adopted."));
732 } else {
733 return array(true, __("The selected packages have been disowned."));
738 * Vote and un-vote for packages
740 * @param array $base_ids Array of package base IDs to vote/un-vote
741 * @param bool $action Votes if true, un-votes if false. Votes by default
743 * @return array Tuple of success/failure indicator and error message
745 function pkgbase_vote ($base_ids, $action=true) {
746 if (!has_credential(CRED_PKGBASE_VOTE)) {
747 if ($action) {
748 return array(false, __("You must be logged in before you can vote for packages."));
749 } else {
750 return array(false, __("You must be logged in before you can un-vote for packages."));
754 $base_ids = sanitize_ids($base_ids);
755 if (empty($base_ids)) {
756 if ($action) {
757 return array(false, __("You did not select any packages to vote for."));
758 } else {
759 return array(false, __("Your votes have been removed from the selected packages."));
763 $dbh = DB::connect();
764 $my_votes = pkgbase_votes_from_sid($_COOKIE["AURSID"]);
765 $uid = uid_from_sid($_COOKIE["AURSID"]);
767 $first = 1;
768 $vote_ids = "";
769 $vote_clauses = "";
770 foreach ($base_ids as $pid) {
771 if ($action) {
772 $check = !isset($my_votes[$pid]);
773 } else {
774 $check = isset($my_votes[$pid]);
777 if ($check) {
778 if ($first) {
779 $first = 0;
780 $vote_ids = $pid;
781 if ($action) {
782 $vote_clauses = "($uid, $pid, " . strval(time()) . ")";
784 } else {
785 $vote_ids .= ", $pid";
786 if ($action) {
787 $vote_clauses .= ", ($uid, $pid, " . strval(time()) . ")";
793 if (!empty($vote_ids)) {
794 /* Only add votes for packages the user hasn't already voted for. */
795 $op = $action ? "+" : "-";
796 $q = "UPDATE PackageBases SET NumVotes = NumVotes $op 1 ";
797 $q.= "WHERE ID IN ($vote_ids)";
799 $dbh->exec($q);
801 if ($action) {
802 $q = "INSERT INTO PackageVotes (UsersID, PackageBaseID, VoteTS) VALUES ";
803 $q.= $vote_clauses;
804 } else {
805 $q = "DELETE FROM PackageVotes WHERE UsersID = $uid ";
806 $q.= "AND PackageBaseID IN ($vote_ids)";
809 $dbh->exec($q);
812 if ($action) {
813 return array(true, __("Your votes have been cast for the selected packages."));
814 } else {
815 return array(true, __("Your votes have been removed from the selected packages."));
820 * Get all usernames and IDs that voted for a specific package base
822 * @param string $pkgbase_name The package base to retrieve votes for
824 * @return array User IDs and usernames that voted for a specific package base
826 function pkgbase_votes_from_name($pkgbase_name) {
827 $dbh = DB::connect();
829 $q = "SELECT UsersID, Username, Name, VoteTS FROM PackageVotes ";
830 $q.= "LEFT JOIN Users ON UsersID = Users.ID ";
831 $q.= "LEFT JOIN PackageBases ";
832 $q.= "ON PackageVotes.PackageBaseID = PackageBases.ID ";
833 $q.= "WHERE PackageBases.Name = ". $dbh->quote($pkgbase_name) . " ";
834 $q.= "ORDER BY Username";
835 $result = $dbh->query($q);
837 if (!$result) {
838 return;
841 $votes = array();
842 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
843 $votes[] = $row;
846 return $votes;
850 * Determine if a user has already voted for a specific package base
852 * @param string $uid The user ID to check for an existing vote
853 * @param string $base_id The package base ID to check for an existing vote
855 * @return bool True if the user has already voted, otherwise false
857 function pkgbase_user_voted($uid, $base_id) {
858 $dbh = DB::connect();
859 $q = "SELECT COUNT(*) FROM PackageVotes WHERE ";
860 $q.= "UsersID = ". $dbh->quote($uid) . " AND ";
861 $q.= "PackageBaseID = " . $dbh->quote($base_id);
862 $result = $dbh->query($q);
863 if (!$result) {
864 return null;
867 return ($result->fetch(PDO::FETCH_COLUMN, 0) > 0);
871 * Determine if a user wants notifications for a specific package base
873 * @param string $uid User ID to check in the database
874 * @param string $base_id Package base ID to check notifications for
876 * @return bool True if the user wants notifications, otherwise false
878 function pkgbase_user_notify($uid, $base_id) {
879 $dbh = DB::connect();
881 $q = "SELECT * FROM PackageNotifications WHERE UserID = " . $dbh->quote($uid);
882 $q.= " AND PackageBaseID = " . $dbh->quote($base_id);
883 $result = $dbh->query($q);
885 if (!$result) {
886 return false;
889 return ($result->fetch(PDO::FETCH_NUM) > 0);
893 * Toggle notification of packages
895 * @param array $base_ids Array of package base IDs to toggle
897 * @return array Tuple of success/failure indicator and error message
899 function pkgbase_notify ($base_ids, $action=true) {
900 if (!has_credential(CRED_PKGBASE_NOTIFY)) {
901 return;
904 $base_ids = sanitize_ids($base_ids);
905 if (empty($base_ids)) {
906 return array(false, __("Couldn't add to notification list."));
909 $dbh = DB::connect();
910 $uid = uid_from_sid($_COOKIE["AURSID"]);
912 $output = "";
914 $first = true;
917 * There currently shouldn't be multiple requests here, but the format
918 * in which it's sent requires this.
920 foreach ($base_ids as $bid) {
921 $q = "SELECT Name FROM PackageBases WHERE ID = $bid";
922 $result = $dbh->query($q);
923 if ($result) {
924 $row = $result->fetch(PDO::FETCH_NUM);
925 $basename = $row[0];
927 else {
928 $basename = '';
931 if ($first)
932 $first = false;
933 else
934 $output .= ", ";
937 if ($action) {
938 $q = "SELECT COUNT(*) FROM PackageNotifications WHERE ";
939 $q .= "UserID = $uid AND PackageBaseID = $bid";
941 /* Notification already added. Don't add again. */
942 $result = $dbh->query($q);
943 if ($result->fetchColumn() == 0) {
944 $q = "INSERT INTO PackageNotifications (PackageBaseID, UserID) VALUES ($bid, $uid)";
945 $dbh->exec($q);
948 $output .= $basename;
950 else {
951 $q = "DELETE FROM PackageNotifications WHERE PackageBaseID = $bid ";
952 $q .= "AND UserID = $uid";
953 $dbh->exec($q);
955 $output .= $basename;
959 if ($action) {
960 $output = __("You have been added to the comment notification list for %s.", $output);
962 else {
963 $output = __("You have been removed from the comment notification list for %s.", $output);
966 return array(true, $output);
970 * Delete a package comment
972 * @param boolean $undelete True if undeleting rather than deleting
973 * @return array Tuple of success/failure indicator and error message
975 function pkgbase_delete_comment($undelete=false) {
976 $uid = uid_from_sid($_COOKIE["AURSID"]);
977 if (!$uid) {
978 return array(false, __("You must be logged in before you can edit package information."));
981 if (isset($_POST["comment_id"])) {
982 $comment_id = $_POST["comment_id"];
983 } else {
984 return array(false, __("Missing comment ID."));
987 $dbh = DB::connect();
988 if ($undelete) {
989 if (!has_credential(CRED_COMMENT_UNDELETE)) {
990 return array(false, __("You are not allowed to undelete this comment."));
993 $q = "UPDATE PackageComments ";
994 $q.= "SET DelUsersID = NULL, ";
995 $q.= "DelTS = NULL ";
996 $q.= "WHERE ID = ".intval($comment_id);
997 $dbh->exec($q);
998 return array(true, __("Comment has been undeleted."));
999 } else {
1000 if (!can_delete_comment($comment_id)) {
1001 return array(false, __("You are not allowed to delete this comment."));
1004 $q = "UPDATE PackageComments ";
1005 $q.= "SET DelUsersID = ".$uid.", ";
1006 $q.= "DelTS = " . strval(time()) . " ";
1007 $q.= "WHERE ID = ".intval($comment_id);
1008 $dbh->exec($q);
1009 return array(true, __("Comment has been deleted."));
1014 * Edit a package comment
1016 * @return array Tuple of success/failure indicator and error message
1018 function pkgbase_edit_comment($comment) {
1019 $uid = uid_from_sid($_COOKIE["AURSID"]);
1020 if (!$uid) {
1021 return array(false, __("You must be logged in before you can edit package information."));
1024 if (isset($_POST["comment_id"])) {
1025 $comment_id = $_POST["comment_id"];
1026 } else {
1027 return array(false, __("Missing comment ID."));
1030 if (trim($comment) == '') {
1031 return array(false, __('Comment cannot be empty.'));
1034 $dbh = DB::connect();
1035 if (can_edit_comment($comment_id)) {
1036 $q = "UPDATE PackageComments ";
1037 $q.= "SET EditedUsersID = ".$uid.", ";
1038 $q.= "Comments = ".$dbh->quote($comment).", ";
1039 $q.= "EditedTS = " . strval(time()) . " ";
1040 $q.= "WHERE ID = ".intval($comment_id);
1041 $dbh->exec($q);
1043 render_comment($comment_id);
1045 return array(true, __("Comment has been edited."));
1046 } else {
1047 return array(false, __("You are not allowed to edit this comment."));
1052 * Get a list of package base keywords
1054 * @param int $base_id The package base ID to retrieve the keywords for
1056 * @return array An array of keywords
1058 function pkgbase_get_keywords($base_id) {
1059 $dbh = DB::connect();
1060 $q = "SELECT Keyword FROM PackageKeywords ";
1061 $q .= "WHERE PackageBaseID = " . intval($base_id) . " ";
1062 $q .= "ORDER BY Keyword ASC";
1063 $result = $dbh->query($q);
1065 if ($result) {
1066 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1067 } else {
1068 return array();
1073 * Update the list of keywords of a package base
1075 * @param int $base_id The package base ID to update the keywords of
1076 * @param array $users Array of keywords
1078 * @return array Tuple of success/failure indicator and error message
1080 function pkgbase_set_keywords($base_id, $keywords) {
1081 $base_id = intval($base_id);
1083 $maintainers = array_merge(array(pkgbase_maintainer_uid($base_id)), pkgbase_get_comaintainer_uids(array($base_id)));
1084 if (!has_credential(CRED_PKGBASE_SET_KEYWORDS, $maintainers)) {
1085 return array(false, __("You are not allowed to edit the keywords of this package base."));
1088 /* Remove empty and duplicate user names. */
1089 $keywords = array_unique(array_filter(array_map('trim', $keywords)));
1091 $dbh = DB::connect();
1093 $q = sprintf("DELETE FROM PackageKeywords WHERE PackageBaseID = %d", $base_id);
1094 $dbh->exec($q);
1096 $i = 0;
1097 foreach ($keywords as $keyword) {
1098 $q = sprintf("INSERT INTO PackageKeywords (PackageBaseID, Keyword) VALUES (%d, %s)", $base_id, $dbh->quote($keyword));
1099 $dbh->exec($q);
1101 $i++;
1102 if ($i >= 20) {
1103 break;
1107 return array(true, __("The package base keywords have been updated."));
1111 * Get a list of package base co-maintainers
1113 * @param int $base_id The package base ID to retrieve the co-maintainers for
1115 * @return array An array of co-maintainer user names
1117 function pkgbase_get_comaintainers($base_id) {
1118 $dbh = DB::connect();
1119 $q = "SELECT UserName FROM PackageComaintainers ";
1120 $q .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
1121 $q .= "WHERE PackageComaintainers.PackageBaseID = " . intval($base_id) . " ";
1122 $q .= "ORDER BY Priority ASC";
1123 $result = $dbh->query($q);
1125 if ($result) {
1126 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1127 } else {
1128 return array();
1133 * Get a list of package base co-maintainer IDs
1135 * @param int $base_id The package base ID to retrieve the co-maintainers for
1137 * @return array An array of co-maintainer user UDs
1139 function pkgbase_get_comaintainer_uids($base_ids) {
1140 $dbh = DB::connect();
1141 $q = "SELECT UsersID FROM PackageComaintainers ";
1142 $q .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
1143 $q .= "WHERE PackageComaintainers.PackageBaseID IN (" . implode(",", $base_ids) . ") ";
1144 $q .= "ORDER BY Priority ASC";
1145 $result = $dbh->query($q);
1147 if ($result) {
1148 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1149 } else {
1150 return array();
1155 * Update the list of co-maintainers of a package base
1157 * @param int $base_id The package base ID to update the co-maintainers of
1158 * @param array $users Array of co-maintainer user names
1159 * @param boolean $override Override credential check if true
1161 * @return array Tuple of success/failure indicator and error message
1163 function pkgbase_set_comaintainers($base_id, $users, $override=false) {
1164 if (!$override && !has_credential(CRED_PKGBASE_EDIT_COMAINTAINERS, array(pkgbase_maintainer_uid($base_id)))) {
1165 return array(false, __("You are not allowed to manage co-maintainers of this package base."));
1168 /* Remove empty and duplicate user names. */
1169 $users = array_unique(array_filter(array_map('trim', $users)));
1171 $dbh = DB::connect();
1173 $uids_new = array();
1174 foreach($users as $user) {
1175 $q = "SELECT ID FROM Users ";
1176 $q .= "WHERE UserName = " . $dbh->quote($user);
1177 $result = $dbh->query($q);
1178 $uid = $result->fetchColumn(0);
1180 if (!$uid) {
1181 return array(false, __("Invalid user name: %s", $user));
1184 $uids_new[] = $uid;
1187 $q = sprintf("SELECT UsersID FROM PackageComaintainers WHERE PackageBaseID = %d", $base_id);
1188 $result = $dbh->query($q);
1189 $uids_old = $result->fetchAll(PDO::FETCH_COLUMN, 0);
1191 $uids_add = array_diff($uids_new, $uids_old);
1192 $uids_rem = array_diff($uids_old, $uids_new);
1194 $i = 1;
1195 foreach ($uids_new as $uid) {
1196 if (in_array($uid, $uids_add)) {
1197 $q = sprintf("INSERT INTO PackageComaintainers (PackageBaseID, UsersID, Priority) VALUES (%d, %d, %d)", $base_id, $uid, $i);
1198 notify(array('comaintainer-add', $base_id, $uid));
1199 } else {
1200 $q = sprintf("UPDATE PackageComaintainers SET Priority = %d WHERE PackageBaseID = %d AND UsersID = %d", $i, $base_id, $uid);
1203 $dbh->exec($q);
1204 $i++;
1207 foreach ($uids_rem as $uid) {
1208 $q = sprintf("DELETE FROM PackageComaintainers WHERE PackageBaseID = %d AND UsersID = %d", $base_id, $uid);
1209 $dbh->exec($q);
1210 notify(array('comaintainer-remove', $base_id, $uid));
1213 return array(true, __("The package base co-maintainers have been updated."));
1216 function pkgbase_remove_comaintainer($base_id, $uid) {
1217 $uname = username_from_id($uid);
1218 $names = pkgbase_get_comaintainers($base_id);
1219 $names = array_diff($names, array($uname));
1220 return pkgbase_set_comaintainers($base_id, $names, true);