Update co-maintainer list when promoting a user
[aur.git] / web / lib / pkgbasefuncs.inc.php
blob327b7f9e30be87d2c3d4374775b37431a536ff23
1 <?php
3 include_once("pkgreqfuncs.inc.php");
5 /**
6 * Get all package categories stored in the database
8 * @param \PDO An already established database connection
10 * @return array All package categories
12 function pkgbase_categories() {
13 $dbh = DB::connect();
14 $q = "SELECT * FROM PackageCategories WHERE ID != 1 ";
15 $q.= "ORDER BY Category ASC";
16 $result = $dbh->query($q);
17 if (!$result) {
18 return null;
21 return $result->fetchAll(PDO::FETCH_KEY_PAIR);
24 /**
25 * Get the number of non-deleted comments for a specific package base
27 * @param string $base_id The package base ID to get comment count for
28 * @param bool $include_deleted True if deleted comments should be included
30 * @return string The number of comments left for a specific package
32 function pkgbase_comments_count($base_id, $include_deleted) {
33 $base_id = intval($base_id);
34 if (!$base_id) {
35 return null;
38 $dbh = DB::connect();
39 $q = "SELECT COUNT(*) FROM PackageComments ";
40 $q.= "WHERE PackageBaseID = " . $base_id . " ";
41 if (!$include_deleted) {
42 $q.= "AND DelUsersID IS NULL";
44 $result = $dbh->query($q);
45 if (!$result) {
46 return null;
49 return $result->fetchColumn(0);
52 /**
53 * Get all package comment information for a specific package base
55 * @param int $base_id The package base ID to get comments for
56 * @param int $limit Maximum number of comments to return (0 means unlimited)
57 * @param bool $include_deleted True if deleted comments should be included
59 * @return array All package comment information for a specific package base
61 function pkgbase_comments($base_id, $limit, $include_deleted) {
62 $base_id = intval($base_id);
63 $limit = intval($limit);
64 if (!$base_id) {
65 return null;
68 $dbh = DB::connect();
69 $q = "SELECT PackageComments.ID, UserName, UsersID, Comments, ";
70 $q.= "CommentTS, DelUsersID FROM PackageComments LEFT JOIN Users ";
71 $q.= "ON PackageComments.UsersID = Users.ID ";
72 $q.= "WHERE PackageBaseID = " . $base_id . " ";
73 if (!$include_deleted) {
74 $q.= "AND DelUsersID IS NULL ";
76 $q.= "ORDER BY CommentTS DESC";
77 if ($limit > 0) {
78 $q.=" LIMIT " . $limit;
80 $result = $dbh->query($q);
81 if (!$result) {
82 return null;
85 return $result->fetchAll();
88 /**
89 * Add a comment to a package page and send out appropriate notifications
91 * @param string $base_id The package base ID to add the comment on
92 * @param string $uid The user ID of the individual who left the comment
93 * @param string $comment The comment left on a package page
95 * @return void
97 function pkgbase_add_comment($base_id, $uid, $comment) {
98 $dbh = DB::connect();
100 $q = "INSERT INTO PackageComments ";
101 $q.= "(PackageBaseID, UsersID, Comments, CommentTS) VALUES (";
102 $q.= intval($base_id) . ", " . $uid . ", ";
103 $q.= $dbh->quote($comment) . ", UNIX_TIMESTAMP())";
104 $dbh->exec($q);
107 * Send e-mail notifications.
108 * TODO: Move notification logic to separate function where it belongs.
110 $q = "SELECT CommentNotify.*, Users.Email ";
111 $q.= "FROM CommentNotify, Users ";
112 $q.= "WHERE Users.ID = CommentNotify.UserID ";
113 $q.= "AND CommentNotify.UserID != " . $uid . " ";
114 $q.= "AND CommentNotify.PackageBaseID = " . intval($base_id);
115 $result = $dbh->query($q);
116 $bcc = array();
118 if ($result) {
119 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
120 array_push($bcc, $row['Email']);
123 $q = "SELECT Name FROM PackageBases WHERE ID = ";
124 $q.= intval($base_id);
125 $result = $dbh->query($q);
126 $row = $result->fetch(PDO::FETCH_ASSOC);
129 * TODO: Add native language emails for users, based on their
130 * preferences. Simply making these strings translatable won't
131 * work, users would be getting emails in the language that the
132 * user who posted the comment was in.
134 $body = 'from ' . get_pkgbase_uri($row['Name'], true) . "\n"
135 . username_from_sid($_COOKIE['AURSID']) . " wrote:\n\n"
136 . $comment
137 . "\n\n---\nIf you no longer wish to receive notifications about this package, please go the the above package page and click the UnNotify button.";
138 $body = wordwrap($body, 70);
139 $bcc = implode(', ', $bcc);
140 $thread_id = "<pkg-notifications-" . $row['Name'] . "@aur.archlinux.org>";
141 $headers = "MIME-Version: 1.0\r\n" .
142 "Content-type: text/plain; charset=UTF-8\r\n" .
143 "Bcc: $bcc\r\n" .
144 "Reply-to: noreply@aur.archlinux.org\r\n" .
145 "From: notify@aur.archlinux.org\r\n" .
146 "In-Reply-To: $thread_id\r\n" .
147 "References: $thread_id\r\n" .
148 "X-Mailer: AUR";
149 @mail('undisclosed-recipients: ;', "AUR Comment for " . $row['Name'], $body, $headers);
154 * Get a list of all packages a logged-in user has voted for
156 * @param string $sid The session ID of the visitor
158 * @return array All packages the visitor has voted for
160 function pkgbase_votes_from_sid($sid="") {
161 $pkgs = array();
162 if (!$sid) {return $pkgs;}
163 $dbh = DB::connect();
164 $q = "SELECT PackageBaseID ";
165 $q.= "FROM PackageVotes, Users, Sessions ";
166 $q.= "WHERE Users.ID = Sessions.UsersID ";
167 $q.= "AND Users.ID = PackageVotes.UsersID ";
168 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
169 $result = $dbh->query($q);
170 if ($result) {
171 while ($row = $result->fetch(PDO::FETCH_NUM)) {
172 $pkgs[$row[0]] = 1;
175 return $pkgs;
179 * Get the package base details
181 * @param string $id The package base ID to get description for
183 * @return array The package base's details OR error message
185 function pkgbase_get_details($base_id) {
186 $dbh = DB::connect();
188 $q = "SELECT PackageBases.ID, PackageBases.Name, ";
189 $q.= "PackageBases.CategoryID, PackageBases.NumVotes, ";
190 $q.= "PackageBases.OutOfDateTS, PackageBases.SubmittedTS, ";
191 $q.= "PackageBases.ModifiedTS, PackageBases.SubmitterUID, ";
192 $q.= "PackageBases.MaintainerUID, PackageBases.PackagerUID, ";
193 $q.= "PackageCategories.Category, ";
194 $q.= "(SELECT COUNT(*) FROM PackageRequests ";
195 $q.= " WHERE PackageRequests.PackageBaseID = PackageBases.ID ";
196 $q.= " AND PackageRequests.Status = 0) AS RequestCount ";
197 $q.= "FROM PackageBases, PackageCategories ";
198 $q.= "WHERE PackageBases.CategoryID = PackageCategories.ID ";
199 $q.= "AND PackageBases.ID = " . intval($base_id);
200 $result = $dbh->query($q);
202 $row = array();
204 if (!$result) {
205 $row['error'] = __("Error retrieving package details.");
207 else {
208 $row = $result->fetch(PDO::FETCH_ASSOC);
209 if (empty($row)) {
210 $row['error'] = __("Package details could not be found.");
214 return $row;
218 * Display the package base details page
220 * @param string $id The package base ID to get details page for
221 * @param array $row Package base details retrieved by pkgbase_get_details()
222 * @param string $SID The session ID of the visitor
224 * @return void
226 function pkgbase_display_details($base_id, $row, $SID="") {
227 $dbh = DB::connect();
229 if (isset($row['error'])) {
230 print "<p>" . $row['error'] . "</p>\n";
232 else {
233 $pkgbase_name = pkgbase_name_from_id($base_id);
235 include('pkgbase_details.php');
237 if ($SID) {
238 include('pkg_comment_form.php');
241 $limit = isset($_GET['comments']) ? 0 : 10;
242 $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED);
243 $comments = pkgbase_comments($base_id, $limit, $include_deleted);
244 if (!empty($comments)) {
245 include('pkg_comments.php');
251 * Convert a list of package IDs into a list of corresponding package bases.
253 * @param array|int $ids Array of package IDs to convert
255 * @return array|int List of package base IDs
257 function pkgbase_from_pkgid($ids) {
258 $dbh = DB::connect();
260 if (is_array($ids)) {
261 $q = "SELECT PackageBaseID FROM Packages ";
262 $q.= "WHERE ID IN (" . implode(",", $ids) . ")";
263 $result = $dbh->query($q);
264 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
265 } else {
266 $q = "SELECT PackageBaseID FROM Packages ";
267 $q.= "WHERE ID = " . $ids;
268 $result = $dbh->query($q);
269 return $result->fetch(PDO::FETCH_COLUMN, 0);
274 * Retrieve ID of a package base by name
276 * @param string $name The package base name to retrieve the ID for
278 * @return int The ID of the package base
280 function pkgbase_from_name($name) {
281 $dbh = DB::connect();
282 $q = "SELECT ID FROM PackageBases WHERE Name = " . $dbh->quote($name);
283 $result = $dbh->query($q);
284 return $result->fetch(PDO::FETCH_COLUMN, 0);
288 * Retrieve the name of a package base given its ID
290 * @param int $base_id The ID of the package base to query
292 * @return string The name of the package base
294 function pkgbase_name_from_id($base_id) {
295 $dbh = DB::connect();
296 $q = "SELECT Name FROM PackageBases WHERE ID = " . intval($base_id);
297 $result = $dbh->query($q);
298 return $result->fetch(PDO::FETCH_COLUMN, 0);
302 * Get the names of all packages belonging to a package base
304 * @param int $base_id The ID of the package base
306 * @return array The names of all packages belonging to the package base
308 function pkgbase_get_pkgnames($base_id) {
309 $dbh = DB::connect();
310 $q = "SELECT Name FROM Packages WHERE PackageBaseID = " . intval($base_id);
311 $result = $dbh->query($q);
312 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
316 * Delete all packages belonging to a package base
318 * @param int $base_id The ID of the package base
320 * @return void
322 function pkgbase_delete_packages($base_id) {
323 $dbh = DB::connect();
324 $q = "DELETE FROM Packages WHERE PackageBaseID = " . intval($base_id);
325 $dbh->exec($q);
329 * Retrieve the maintainer of a package base given its ID
331 * @param int $base_id The ID of the package base to query
333 * @return int The user ID of the current package maintainer
335 function pkgbase_maintainer_uid($base_id) {
336 $dbh = DB::connect();
337 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID = " . intval($base_id);
338 $result = $dbh->query($q);
339 return $result->fetch(PDO::FETCH_COLUMN, 0);
344 * Flag package(s) as out-of-date
346 * @param array $base_ids Array of package base IDs to flag/unflag
348 * @return array Tuple of success/failure indicator and error message
350 function pkgbase_flag($base_ids) {
351 if (!has_credential(CRED_PKGBASE_FLAG)) {
352 return array(false, __("You must be logged in before you can flag packages."));
355 $base_ids = sanitize_ids($base_ids);
356 if (empty($base_ids)) {
357 return array(false, __("You did not select any packages to flag."));
360 $dbh = DB::connect();
362 $q = "UPDATE PackageBases SET";
363 $q.= " OutOfDateTS = UNIX_TIMESTAMP()";
364 $q.= " WHERE ID IN (" . implode(",", $base_ids) . ")";
365 $q.= " AND OutOfDateTS IS NULL";
367 $affected_pkgs = $dbh->exec($q);
369 if ($affected_pkgs > 0) {
370 /* Notify of flagging by e-mail. */
371 $f_name = username_from_sid($_COOKIE['AURSID']);
372 $f_email = email_from_sid($_COOKIE['AURSID']);
373 $f_uid = uid_from_sid($_COOKIE['AURSID']);
374 $q = "SELECT PackageBases.Name, Users.Email ";
375 $q.= "FROM PackageBases, Users ";
376 $q.= "WHERE PackageBases.ID IN (" . implode(",", $base_ids) .") ";
377 $q.= "AND Users.ID = PackageBases.MaintainerUID ";
378 $q.= "AND Users.ID != " . $f_uid;
379 $result = $dbh->query($q);
380 if ($result) {
381 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
382 $body = "Your package " . $row['Name'] . " has been flagged out of date by " . $f_name . " [1]. You may view your package at:\n" . get_pkgbase_uri($row['Name'], true) . "\n\n[1] - " . get_user_uri($f_name, true);
383 $body = wordwrap($body, 70);
384 $headers = "MIME-Version: 1.0\r\n" .
385 "Content-type: text/plain; charset=UTF-8\r\n" .
386 "Reply-to: noreply@aur.archlinux.org\r\n" .
387 "From: notify@aur.archlinux.org\r\n" .
388 "X-Mailer: PHP\r\n" .
389 "X-MimeOLE: Produced By AUR";
390 @mail($row['Email'], "AUR Out-of-date Notification for ".$row['Name'], $body, $headers);
395 return array(true, __("The selected packages have been flagged out-of-date."));
399 * Unflag package(s) as out-of-date
401 * @param array $base_ids Array of package base IDs to flag/unflag
403 * @return array Tuple of success/failure indicator and error message
405 function pkgbase_unflag($base_ids) {
406 $uid = uid_from_sid($_COOKIE["AURSID"]);
407 if (!$uid) {
408 return array(false, __("You must be logged in before you can unflag packages."));
411 $base_ids = sanitize_ids($base_ids);
412 if (empty($base_ids)) {
413 return array(false, __("You did not select any packages to unflag."));
416 $dbh = DB::connect();
418 $q = "UPDATE PackageBases SET ";
419 $q.= "OutOfDateTS = NULL ";
420 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
422 if (!has_credential(CRED_PKGBASE_UNFLAG)) {
423 $q.= "AND MaintainerUID = " . $uid;
426 $result = $dbh->exec($q);
428 if ($result) {
429 return array(true, __("The selected packages have been unflagged."));
434 * Delete package bases
436 * @param array $base_ids Array of package base IDs to delete
437 * @param int $merge_base_id Package base to merge the deleted ones into
438 * @param int $via Package request to close upon deletion
439 * @param bool $grant Allow anyone to delete the package base
441 * @return array Tuple of success/failure indicator and error message
443 function pkgbase_delete ($base_ids, $merge_base_id, $via, $grant=false) {
444 if (!$grant && !has_credential(CRED_PKGBASE_DELETE)) {
445 return array(false, __("You do not have permission to delete packages."));
448 $base_ids = sanitize_ids($base_ids);
449 if (empty($base_ids)) {
450 return array(false, __("You did not select any packages to delete."));
453 $dbh = DB::connect();
455 if ($merge_base_id) {
456 $merge_base_name = pkgbase_name_from_id($merge_base_id);
459 /* Send e-mail notifications. */
460 foreach ($base_ids as $base_id) {
461 $q = "SELECT CommentNotify.*, Users.Email ";
462 $q.= "FROM CommentNotify, Users ";
463 $q.= "WHERE Users.ID = CommentNotify.UserID ";
464 $q.= "AND CommentNotify.UserID != " . uid_from_sid($_COOKIE['AURSID']) . " ";
465 $q.= "AND CommentNotify.PackageBaseID = " . $base_id;
466 $result = $dbh->query($q);
467 $bcc = array();
469 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
470 array_push($bcc, $row['Email']);
472 if (!empty($bcc)) {
473 $pkgbase_name = pkgbase_name_from_id($base_id);
476 * TODO: Add native language emails for users, based on
477 * their preferences. Simply making these strings
478 * translatable won't work, users would be getting
479 * emails in the language that the user who posted the
480 * comment was in.
482 $body = "";
483 if ($merge_base_id) {
484 $body .= username_from_sid($_COOKIE['AURSID']) . " merged \"".$pkgbase_name."\" into \"$merge_base_name\".\n\n";
485 $body .= "You will no longer receive notifications about this package, please go to https://aur.archlinux.org" . get_pkgbase_uri($merge_base_name) . " and click the Notify button if you wish to recieve them again.";
486 } else {
487 $body .= username_from_sid($_COOKIE['AURSID']) . " deleted \"".$pkgbase_name."\".\n\n";
488 $body .= "You will no longer receive notifications about this package.";
490 $body = wordwrap($body, 70);
491 $bcc = implode(', ', $bcc);
492 $headers = "MIME-Version: 1.0\r\n" .
493 "Content-type: text/plain; charset=UTF-8\r\n" .
494 "Bcc: $bcc\r\n" .
495 "Reply-to: noreply@aur.archlinux.org\r\n" .
496 "From: notify@aur.archlinux.org\r\n" .
497 "X-Mailer: AUR";
498 @mail('undisclosed-recipients: ;', "AUR Package deleted: " . $pkgbase_name, $body, $headers);
503 * Close package request if the deletion was initiated through the
504 * request interface. NOTE: This needs to happen *before* the actual
505 * deletion. Otherwise, the former maintainer will not be included in
506 * the Cc list of the request notification email.
508 if ($via) {
509 pkgreq_close(intval($via), 'accepted', '');
512 /* Scan through pending deletion requests and close them. */
513 if (!$action) {
514 $username = username_from_sid($_COOKIE['AURSID']);
515 foreach ($base_ids as $base_id) {
516 $pkgreq_ids = array_merge(pkgreq_by_pkgbase($base_id));
517 foreach ($pkgreq_ids as $pkgreq_id) {
518 pkgreq_close(intval($pkgreq_id), 'accepted',
519 'The user ' . $username .
520 ' deleted the package.', true);
525 if ($merge_base_id) {
526 /* Merge comments */
527 $q = "UPDATE PackageComments ";
528 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
529 $q.= "WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
530 $dbh->exec($q);
532 /* Merge votes */
533 foreach ($base_ids as $base_id) {
534 $q = "UPDATE PackageVotes ";
535 $q.= "SET PackageBaseID = " . intval($merge_base_id) . " ";
536 $q.= "WHERE PackageBaseID = " . $base_id . " ";
537 $q.= "AND UsersID NOT IN (";
538 $q.= "SELECT * FROM (SELECT UsersID ";
539 $q.= "FROM PackageVotes ";
540 $q.= "WHERE PackageBaseID = " . intval($merge_base_id);
541 $q.= ") temp)";
542 $dbh->exec($q);
545 $q = "UPDATE PackageBases ";
546 $q.= "SET NumVotes = (SELECT COUNT(*) FROM PackageVotes ";
547 $q.= "WHERE PackageBaseID = " . intval($merge_base_id) . ") ";
548 $q.= "WHERE ID = " . intval($merge_base_id);
549 $dbh->exec($q);
552 $q = "DELETE FROM Packages WHERE PackageBaseID IN (" . implode(",", $base_ids) . ")";
553 $dbh->exec($q);
555 $q = "DELETE FROM PackageBases WHERE ID IN (" . implode(",", $base_ids) . ")";
556 $dbh->exec($q);
558 return array(true, __("The selected packages have been deleted."));
562 * Adopt or disown packages
564 * @param array $base_ids Array of package base IDs to adopt/disown
565 * @param bool $action Adopts if true, disowns if false. Adopts by default
566 * @param int $via Package request to close upon adoption
568 * @return array Tuple of success/failure indicator and error message
570 function pkgbase_adopt ($base_ids, $action=true, $via) {
571 $dbh = DB::connect();
573 $uid = uid_from_sid($_COOKIE["AURSID"]);
574 if (!$uid) {
575 if ($action) {
576 return array(false, __("You must be logged in before you can adopt packages."));
577 } else {
578 return array(false, __("You must be logged in before you can disown packages."));
582 /* Verify package ownership. */
583 $base_ids = sanitize_ids($base_ids);
585 $q = "SELECT ID FROM PackageBases ";
586 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
588 if ($action && !has_credential(CRED_PKGBASE_ADOPT)) {
589 /* Regular users may only adopt orphan packages. */
590 $q.= "AND MaintainerUID IS NULL";
592 if (!$action && !has_credential(CRED_PKGBASE_DISOWN)) {
593 /* Regular users may only disown their own packages. */
594 $q.= "AND MaintainerUID = " . $uid;
597 $result = $dbh->query($q);
598 $base_ids = $result->fetchAll(PDO::FETCH_COLUMN, 0);
600 /* Error out if the list of remaining packages is empty. */
601 if (empty($base_ids)) {
602 if ($action) {
603 return array(false, __("You did not select any packages to adopt."));
604 } else {
605 return array(false, __("You did not select any packages to disown."));
610 * Close package request if the disownment was initiated through the
611 * request interface. NOTE: This needs to happen *before* the actual
612 * disown operation. Otherwise, the former maintainer will not be
613 * included in the Cc list of the request notification email.
615 if ($via) {
616 pkgreq_close(intval($via), 'accepted', '');
619 /* Scan through pending orphan requests and close them. */
620 if (!$action) {
621 $username = username_from_sid($_COOKIE['AURSID']);
622 foreach ($base_ids as $base_id) {
623 $pkgreq_ids = pkgreq_by_pkgbase($base_id, 'orphan');
624 foreach ($pkgreq_ids as $pkgreq_id) {
625 pkgreq_close(intval($pkgreq_id), 'accepted',
626 'The user ' . $username .
627 ' disowned the package.', true);
632 /* Adopt or disown the package. */
633 if ($action) {
634 $q = "UPDATE PackageBases ";
635 $q.= "SET MaintainerUID = $uid ";
636 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
637 $dbh->exec($q);
638 } else {
639 /* Update the co-maintainer list when disowning a package. */
640 if (has_credential(CRED_PKGBASE_DISOWN)) {
641 foreach ($base_ids as $base_id) {
642 pkgbase_set_comaintainers($base_id, array());
645 $q = "UPDATE PackageBases ";
646 $q.= "SET MaintainerUID = NULL ";
647 $q.= "WHERE ID IN (" . implode(",", $base_ids) . ") ";
648 $dbh->exec($q);
649 } else {
650 foreach ($base_ids as $base_id) {
651 $comaintainers = pkgbase_get_comaintainers($base_id);
653 if (count($comaintainers) > 0) {
654 $uid = uid_from_username($comaintainers[0]);
655 $comaintainers = array_diff($comaintainers, array($comaintainers[0]));
656 pkgbase_set_comaintainers($base_id, $comaintainers);
657 } else {
658 $uid = "NULL";
661 $q = "UPDATE PackageBases ";
662 $q.= "SET MaintainerUID = " . $uid . " ";
663 $q.= "WHERE ID = " . $base_id;
664 $dbh->exec($q);
669 if ($action) {
670 pkgbase_notify($base_ids);
671 return array(true, __("The selected packages have been adopted."));
672 } else {
673 return array(true, __("The selected packages have been disowned."));
678 * Vote and un-vote for packages
680 * @param array $base_ids Array of package base IDs to vote/un-vote
681 * @param bool $action Votes if true, un-votes if false. Votes by default
683 * @return array Tuple of success/failure indicator and error message
685 function pkgbase_vote ($base_ids, $action=true) {
686 if (!has_credential(CRED_PKGBASE_VOTE)) {
687 if ($action) {
688 return array(false, __("You must be logged in before you can vote for packages."));
689 } else {
690 return array(false, __("You must be logged in before you can un-vote for packages."));
694 $base_ids = sanitize_ids($base_ids);
695 if (empty($base_ids)) {
696 if ($action) {
697 return array(false, __("You did not select any packages to vote for."));
698 } else {
699 return array(false, __("Your votes have been removed from the selected packages."));
703 $dbh = DB::connect();
704 $my_votes = pkgbase_votes_from_sid($_COOKIE["AURSID"]);
705 $uid = uid_from_sid($_COOKIE["AURSID"]);
707 $first = 1;
708 foreach ($base_ids as $pid) {
709 if ($action) {
710 $check = !isset($my_votes[$pid]);
711 } else {
712 $check = isset($my_votes[$pid]);
715 if ($check) {
716 if ($first) {
717 $first = 0;
718 $vote_ids = $pid;
719 if ($action) {
720 $vote_clauses = "($uid, $pid, UNIX_TIMESTAMP())";
722 } else {
723 $vote_ids .= ", $pid";
724 if ($action) {
725 $vote_clauses .= ", ($uid, $pid, UNIX_TIMESTAMP())";
731 /* Only add votes for packages the user hasn't already voted for. */
732 $op = $action ? "+" : "-";
733 $q = "UPDATE PackageBases SET NumVotes = NumVotes $op 1 ";
734 $q.= "WHERE ID IN ($vote_ids)";
736 $dbh->exec($q);
738 if ($action) {
739 $q = "INSERT INTO PackageVotes (UsersID, PackageBaseID, VoteTS) VALUES ";
740 $q.= $vote_clauses;
741 } else {
742 $q = "DELETE FROM PackageVotes WHERE UsersID = $uid ";
743 $q.= "AND PackageBaseID IN ($vote_ids)";
746 $dbh->exec($q);
748 if ($action) {
749 return array(true, __("Your votes have been cast for the selected packages."));
750 } else {
751 return array(true, __("Your votes have been removed from the selected packages."));
756 * Get all usernames and IDs that voted for a specific package base
758 * @param string $pkgbase_name The package base to retrieve votes for
760 * @return array User IDs and usernames that voted for a specific package base
762 function pkgbase_votes_from_name($pkgbase_name) {
763 $dbh = DB::connect();
765 $q = "SELECT UsersID, Username, Name, VoteTS FROM PackageVotes ";
766 $q.= "LEFT JOIN Users ON UsersID = Users.ID ";
767 $q.= "LEFT JOIN PackageBases ";
768 $q.= "ON PackageVotes.PackageBaseID = PackageBases.ID ";
769 $q.= "WHERE PackageBases.Name = ". $dbh->quote($pkgbase_name) . " ";
770 $q.= "ORDER BY Username";
771 $result = $dbh->query($q);
773 if (!$result) {
774 return;
777 $votes = array();
778 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
779 $votes[] = $row;
782 return $votes;
786 * Determine if a user has already voted for a specific package base
788 * @param string $uid The user ID to check for an existing vote
789 * @param string $base_id The package base ID to check for an existing vote
791 * @return bool True if the user has already voted, otherwise false
793 function pkgbase_user_voted($uid, $base_id) {
794 $dbh = DB::connect();
795 $q = "SELECT COUNT(*) FROM PackageVotes WHERE ";
796 $q.= "UsersID = ". $dbh->quote($uid) . " AND ";
797 $q.= "PackageBaseID = " . $dbh->quote($base_id);
798 $result = $dbh->query($q);
799 if (!$result) {
800 return null;
803 return ($result->fetch(PDO::FETCH_COLUMN, 0) > 0);
807 * Determine if a user wants notifications for a specific package base
809 * @param string $uid User ID to check in the database
810 * @param string $base_id Package base ID to check notifications for
812 * @return bool True if the user wants notifications, otherwise false
814 function pkgbase_user_notify($uid, $base_id) {
815 $dbh = DB::connect();
817 $q = "SELECT * FROM CommentNotify WHERE UserID = " . $dbh->quote($uid);
818 $q.= " AND PackageBaseID = " . $dbh->quote($base_id);
819 $result = $dbh->query($q);
821 if ($result->fetch(PDO::FETCH_NUM)) {
822 return true;
824 else {
825 return false;
830 * Toggle notification of packages
832 * @param array $base_ids Array of package base IDs to toggle
834 * @return array Tuple of success/failure indicator and error message
836 function pkgbase_notify ($base_ids, $action=true) {
837 if (!has_credential(CRED_PKGBASE_NOTIFY)) {
838 return;
841 $base_ids = sanitize_ids($base_ids);
842 if (empty($base_ids)) {
843 return array(false, __("Couldn't add to notification list."));
846 $dbh = DB::connect();
847 $uid = uid_from_sid($_COOKIE["AURSID"]);
849 $output = "";
851 $first = true;
854 * There currently shouldn't be multiple requests here, but the format
855 * in which it's sent requires this.
857 foreach ($base_ids as $bid) {
858 $q = "SELECT Name FROM PackageBases WHERE ID = $bid";
859 $result = $dbh->query($q);
860 if ($result) {
861 $row = $result->fetch(PDO::FETCH_NUM);
862 $basename = $row[0];
864 else {
865 $basename = '';
868 if ($first)
869 $first = false;
870 else
871 $output .= ", ";
874 if ($action) {
875 $q = "SELECT COUNT(*) FROM CommentNotify WHERE ";
876 $q .= "UserID = $uid AND PackageBaseID = $bid";
878 /* Notification already added. Don't add again. */
879 $result = $dbh->query($q);
880 if ($result->fetchColumn() == 0) {
881 $q = "INSERT INTO CommentNotify (PackageBaseID, UserID) VALUES ($bid, $uid)";
882 $dbh->exec($q);
885 $output .= $basename;
887 else {
888 $q = "DELETE FROM CommentNotify WHERE PackageBaseID = $bid ";
889 $q .= "AND UserID = $uid";
890 $dbh->exec($q);
892 $output .= $basename;
896 if ($action) {
897 $output = __("You have been added to the comment notification list for %s.", $output);
899 else {
900 $output = __("You have been removed from the comment notification list for %s.", $output);
903 return array(true, $output);
907 * Delete a package comment
909 * @return array Tuple of success/failure indicator and error message
911 function pkgbase_delete_comment() {
912 $uid = uid_from_sid($_COOKIE["AURSID"]);
913 if (!$uid) {
914 return array(false, __("You must be logged in before you can edit package information."));
917 if (isset($_POST["comment_id"])) {
918 $comment_id = $_POST["comment_id"];
919 } else {
920 return array(false, __("Missing comment ID."));
923 $dbh = DB::connect();
924 if (can_delete_comment($comment_id)) {
925 $q = "UPDATE PackageComments ";
926 $q.= "SET DelUsersID = ".$uid." ";
927 $q.= "WHERE ID = ".intval($comment_id);
928 $dbh->exec($q);
929 return array(true, __("Comment has been deleted."));
930 } else {
931 return array(false, __("You are not allowed to delete this comment."));
936 * Change package base category
938 * @param int Package base ID of the package base to modify
940 * @return array Tuple of success/failure indicator and error message
942 function pkgbase_change_category($base_id) {
943 $uid = uid_from_sid($_COOKIE["AURSID"]);
944 if (!$uid) {
945 return array(false, __("You must be logged in before you can edit package information."));
948 if (isset($_POST["category_id"])) {
949 $category_id = $_POST["category_id"];
950 } else {
951 return array(false, __("Missing category ID."));
954 $dbh = DB::connect();
955 $catArray = pkgbase_categories($dbh);
956 if (!array_key_exists($category_id, $catArray)) {
957 return array(false, __("Invalid category ID."));
960 $base_id = intval($base_id);
962 /* Verify package ownership. */
963 $q = "SELECT MaintainerUID FROM PackageBases WHERE ID = " . $base_id;
964 $result = $dbh->query($q);
965 if ($result) {
966 $row = $result->fetch(PDO::FETCH_ASSOC);
969 if (!$result || !has_credential(CRED_PKGBASE_CHANGE_CATEGORY, array($row["MaintainerUID"]))) {
970 return array(false, __("You are not allowed to change this package category."));
973 $q = "UPDATE PackageBases ";
974 $q.= "SET CategoryID = ".intval($category_id)." ";
975 $q.= "WHERE ID = ".intval($base_id);
976 $dbh->exec($q);
977 return array(true, __("Package category changed."));
981 * Change the category a package base belongs to
983 * @param int $base_id The package base ID to change the category for
984 * @param int $category_id The new category ID for the package
986 * @return void
988 function pkgbase_update_category($base_id, $category_id) {
989 $dbh = DB::connect();
990 $q = sprintf("UPDATE PackageBases SET CategoryID = %d WHERE ID = %d",
991 $category_id, $base_id);
992 $dbh->exec($q);
996 * Get a list of package base co-maintainers
998 * @param int $base_id The package base ID to retrieve the co-maintainers for
1000 * @return array An array of co-maintainer user names
1002 function pkgbase_get_comaintainers($base_id) {
1003 $dbh = DB::connect();
1004 $q = "SELECT UserName FROM PackageComaintainers ";
1005 $q .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
1006 $q .= "WHERE PackageComaintainers.PackageBaseID = " . intval($base_id) . " ";
1007 $q .= "ORDER BY Priority ASC";
1008 $result = $dbh->query($q);
1010 if ($result) {
1011 return $result->fetchAll(PDO::FETCH_COLUMN, 0);
1012 } else {
1013 return array();
1018 * Update the list of co-maintainers of a package base
1020 * @param int $base_id The package base ID to update the co-maintainers of
1021 * @param array $users Array of co-maintainer user names
1023 * @return array Tuple of success/failure indicator and error message
1025 function pkgbase_set_comaintainers($base_id, $users) {
1026 if (!has_credential(CRED_PKGBASE_EDIT_COMAINTAINERS, array(pkgbase_maintainer_uid($base_id)))) {
1027 return array(false, __("You are not allowed to manage co-maintainers of this package base."));
1030 /* Remove empty and duplicate user names. */
1031 $users = array_unique(array_filter(array_map('trim', $users)));
1033 $dbh = DB::connect();
1035 $uids = array();
1036 foreach($users as $user) {
1037 $q = "SELECT ID FROM Users ";
1038 $q .= "WHERE UserName = " . $dbh->quote($user);
1039 $result = $dbh->query($q);
1040 $uid = $result->fetchColumn(0);
1042 if (!$uid) {
1043 return array(false, __("Invalid user name: %s", $user));
1046 $uids[] = $uid;
1049 $q = sprintf("DELETE FROM PackageComaintainers WHERE PackageBaseID = %d", $base_id);
1050 $dbh->exec($q);
1052 $i = 1;
1053 foreach ($uids as $uid) {
1054 $q = sprintf("INSERT INTO PackageComaintainers (PackageBaseID, UsersID, Priority) VALUES (%d, %d, %d)", $base_id, $uid, $i);
1055 $dbh->exec($q);
1056 $i++;
1059 return array(true, __("The package base co-maintainers have been updated."));