Allow package co-maintainers to pin comments
[aur.git] / web / lib / pkgfuncs.inc.php
blobdbcf63e83d68694e03808bccc0408f58fd99a074
1 <?php
3 include_once("pkgbasefuncs.inc.php");
5 /**
6 * Determine if the user can delete a specific package comment
8 * Only the comment submitter, Trusted Users, and Developers can delete
9 * comments. This function is used for the backend side of comment deletion.
11 * @param string $comment_id The comment ID in the database
13 * @return bool True if the user can delete the comment, otherwise false
15 function can_delete_comment($comment_id=0) {
16 $dbh = DB::connect();
18 $q = "SELECT UsersID FROM PackageComments ";
19 $q.= "WHERE ID = " . intval($comment_id);
20 $result = $dbh->query($q);
22 if (!$result) {
23 return false;
26 $uid = $result->fetch(PDO::FETCH_COLUMN, 0);
28 return has_credential(CRED_COMMENT_DELETE, array($uid));
31 /**
32 * Determine if the user can delete a specific package comment using an array
34 * Only the comment submitter, Trusted Users, and Developers can delete
35 * comments. This function is used for the frontend side of comment deletion.
37 * @param array $comment All database information relating a specific comment
39 * @return bool True if the user can delete the comment, otherwise false
41 function can_delete_comment_array($comment) {
42 return has_credential(CRED_COMMENT_DELETE, array($comment['UsersID']));
45 /**
46 * Determine if the user can edit a specific package comment
48 * Only the comment submitter, Trusted Users, and Developers can edit
49 * comments. This function is used for the backend side of comment editing.
51 * @param string $comment_id The comment ID in the database
53 * @return bool True if the user can edit the comment, otherwise false
55 function can_edit_comment($comment_id=0) {
56 $dbh = DB::connect();
58 $q = "SELECT UsersID FROM PackageComments ";
59 $q.= "WHERE ID = " . intval($comment_id);
60 $result = $dbh->query($q);
62 if (!$result) {
63 return false;
66 $uid = $result->fetch(PDO::FETCH_COLUMN, 0);
68 return has_credential(CRED_COMMENT_EDIT, array($uid));
71 /**
72 * Determine if the user can edit a specific package comment using an array
74 * Only the comment submitter, Trusted Users, and Developers can edit
75 * comments. This function is used for the frontend side of comment editing.
77 * @param array $comment All database information relating a specific comment
79 * @return bool True if the user can edit the comment, otherwise false
81 function can_edit_comment_array($comment) {
82 return has_credential(CRED_COMMENT_EDIT, array($comment['UsersID']));
85 /**
86 * Determine if the user can pin a specific package comment
88 * Only the Package Maintainer, Package Co-maintainers, Trusted Users, and
89 * Developers can pin comments. This function is used for the backend side of
90 * comment pinning.
92 * @param string $comment_id The comment ID in the database
94 * @return bool True if the user can pin the comment, otherwise false
96 function can_pin_comment($comment_id=0) {
97 $dbh = DB::connect();
99 $q = "SELECT MaintainerUID FROM PackageBases AS pb ";
100 $q.= "LEFT JOIN PackageComments AS pc ON pb.ID = pc.PackageBaseID ";
101 $q.= "WHERE pc.ID = " . intval($comment_id) . " ";
102 $q.= "UNION ";
103 $q = "SELECT pcm.UsersID FROM PackageComaintainers AS pcm ";
104 $q.= "LEFT JOIN PackageComments AS pc ";
105 $q.= "ON pcm.PackageBaseID = pc.PackageBaseID ";
106 $q.= "WHERE pc.ID = " . intval($comment_id);
107 $result = $dbh->query($q);
109 if (!$result) {
110 return false;
113 $uids = $result->fetchAll(PDO::FETCH_COLUMN, 0);
115 return has_credential(CRED_COMMENT_PIN, $uids);
119 * Determine if the user can edit a specific package comment using an array
121 * Only the Package Maintainer, Package Co-maintainers, Trusted Users, and
122 * Developers can pin comments. This function is used for the frontend side of
123 * comment pinning.
125 * @param array $comment All database information relating a specific comment
127 * @return bool True if the user can edit the comment, otherwise false
129 function can_pin_comment_array($comment) {
130 return can_pin_comment($comment['ID']);
134 * Check to see if the package name already exists in the database
136 * @param string $name The package name to check
138 * @return string|void Package name if it already exists
140 function pkg_from_name($name="") {
141 if (!$name) {return NULL;}
142 $dbh = DB::connect();
143 $q = "SELECT ID FROM Packages ";
144 $q.= "WHERE Name = " . $dbh->quote($name);
145 $result = $dbh->query($q);
146 if (!$result) {
147 return;
149 $row = $result->fetch(PDO::FETCH_NUM);
150 return $row[0];
154 * Get licenses for a specific package
156 * @param int $pkgid The package to get licenses for
158 * @return array All licenses for the package
160 function pkg_licenses($pkgid) {
161 $lics = array();
162 $pkgid = intval($pkgid);
163 if ($pkgid > 0) {
164 $dbh = DB::connect();
165 $q = "SELECT l.Name FROM Licenses l ";
166 $q.= "INNER JOIN PackageLicenses pl ON pl.LicenseID = l.ID ";
167 $q.= "WHERE pl.PackageID = ". $pkgid;
168 $result = $dbh->query($q);
169 if (!$result) {
170 return array();
172 while ($row = $result->fetch(PDO::FETCH_COLUMN, 0)) {
173 $lics[] = $row;
176 return $lics;
180 * Get package groups for a specific package
182 * @param int $pkgid The package to get groups for
184 * @return array All package groups for the package
186 function pkg_groups($pkgid) {
187 $grps = array();
188 $pkgid = intval($pkgid);
189 if ($pkgid > 0) {
190 $dbh = DB::connect();
191 $q = "SELECT g.Name FROM Groups g ";
192 $q.= "INNER JOIN PackageGroups pg ON pg.GroupID = g.ID ";
193 $q.= "WHERE pg.PackageID = ". $pkgid;
194 $result = $dbh->query($q);
195 if (!$result) {
196 return array();
198 while ($row = $result->fetch(PDO::FETCH_COLUMN, 0)) {
199 $grps[] = $row;
202 return $grps;
206 * Get providers for a specific package
208 * @param string $name The name of the "package" to get providers for
210 * @return array The IDs and names of all providers of the package
212 function pkg_providers($name) {
213 $dbh = DB::connect();
214 $q = "SELECT p.ID, p.Name FROM Packages p ";
215 $q.= "LEFT JOIN PackageRelations pr ON pr.PackageID = p.ID ";
216 $q.= "LEFT JOIN RelationTypes rt ON rt.ID = pr.RelTypeID ";
217 $q.= "WHERE p.Name = " . $dbh->quote($name) . " ";
218 $q.= "OR (rt.Name = 'provides' ";
219 $q.= "AND pr.RelName = " . $dbh->quote($name) . ")";
220 $q.= "UNION ";
221 $q.= "SELECT 0, Name FROM OfficialProviders ";
222 $q.= "WHERE Provides = " . $dbh->quote($name);
223 $result = $dbh->query($q);
225 if (!$result) {
226 return array();
229 $providers = array();
230 while ($row = $result->fetch(PDO::FETCH_NUM)) {
231 $providers[] = $row;
233 return $providers;
237 * Get package dependencies for a specific package
239 * @param int $pkgid The package to get dependencies for
240 * @param int $limit An upper bound for the number of packages to retrieve
242 * @return array All package dependencies for the package
244 function pkg_dependencies($pkgid, $limit) {
245 $deps = array();
246 $pkgid = intval($pkgid);
247 if ($pkgid > 0) {
248 $dbh = DB::connect();
249 $q = "SELECT pd.DepName, dt.Name, pd.DepDesc, ";
250 $q.= "pd.DepCondition, pd.DepArch, p.ID ";
251 $q.= "FROM PackageDepends pd ";
252 $q.= "LEFT JOIN Packages p ON pd.DepName = p.Name ";
253 $q.= "LEFT JOIN DependencyTypes dt ON dt.ID = pd.DepTypeID ";
254 $q.= "WHERE pd.PackageID = ". $pkgid . " ";
255 $q.= "ORDER BY pd.DepName LIMIT " . intval($limit);
256 $result = $dbh->query($q);
257 if (!$result) {
258 return array();
260 while ($row = $result->fetch(PDO::FETCH_NUM)) {
261 $deps[] = $row;
264 return $deps;
268 * Get package relations for a specific package
270 * @param int $pkgid The package to get relations for
272 * @return array All package relations for the package
274 function pkg_relations($pkgid) {
275 $rels = array();
276 $pkgid = intval($pkgid);
277 if ($pkgid > 0) {
278 $dbh = DB::connect();
279 $q = "SELECT pr.RelName, rt.Name, pr.RelCondition, pr.RelArch, p.ID FROM PackageRelations pr ";
280 $q.= "LEFT JOIN Packages p ON pr.RelName = p.Name ";
281 $q.= "LEFT JOIN RelationTypes rt ON rt.ID = pr.RelTypeID ";
282 $q.= "WHERE pr.PackageID = ". $pkgid . " ";
283 $q.= "ORDER BY pr.RelName";
284 $result = $dbh->query($q);
285 if (!$result) {
286 return array();
288 while ($row = $result->fetch(PDO::FETCH_NUM)) {
289 $rels[] = $row;
292 return $rels;
296 * Get the HTML code to display a package dependency link annotation
297 * (dependency type, architecture, ...)
299 * @param string $type The name of the dependency type
300 * @param string $arch The package dependency architecture
301 * @param string $desc An optdepends description
303 * @return string The HTML code of the label to display
305 function pkg_deplink_annotation($type, $arch, $desc=false) {
306 if ($type == 'depends' && !$arch) {
307 return '';
310 $link = ' <em>(';
312 if ($type == 'makedepends') {
313 $link .= 'make';
314 } elseif ($type == 'checkdepends') {
315 $link .= 'check';
316 } elseif ($type == 'optdepends') {
317 $link .= 'optional';
320 if ($type != 'depends' && $arch) {
321 $link .= ', ';
324 if ($arch) {
325 $link .= htmlspecialchars($arch);
328 $link .= ')';
329 if ($type == 'optdepends' && $desc) {
330 $link .= ' &ndash; ' . htmlspecialchars($desc) . ' </em>';
332 $link .= '</em>';
334 return $link;
338 * Get the HTML code to display a package provider link
340 * @param string $name The name of the provider
341 * @param bool $official True if the package is in the official repositories
343 * @return string The HTML code of the link to display
345 function pkg_provider_link($name, $official) {
346 $link = '<a href="';
347 if ($official) {
348 $link .= 'https://www.archlinux.org/packages/?q=' .
349 urlencode($name);
350 } else {
351 $link .= htmlspecialchars(get_pkg_uri($name), ENT_QUOTES);
353 $link .= '" title="' . __('View packages details for') . ' ';
354 $link .= htmlspecialchars($name) . '">';
355 $link .= htmlspecialchars($name) . '</a>';
357 return $link;
361 * Get the HTML code to display a package dependency link
363 * @param string $name The name of the dependency
364 * @param string $type The name of the dependency type
365 * @param string $desc The (optional) description of the dependency
366 * @param string $cond The package dependency condition string
367 * @param string $arch The package dependency architecture
368 * @param int $pkg_id The package of the package to display the dependency for
370 * @return string The HTML code of the label to display
372 function pkg_depend_link($name, $type, $desc, $cond, $arch, $pkg_id) {
374 * TODO: We currently perform one SQL query per nonexistent package
375 * dependency. It would be much better if we could annotate dependency
376 * data with providers so that we already know whether a dependency is
377 * a "provision name" or a package from the official repositories at
378 * this point.
380 $providers = pkg_providers($name);
382 if (count($providers) == 0) {
383 $link = '<span class="broken">';
384 $link .= htmlspecialchars($name);
385 $link .= '</span>';
386 $link .= htmlspecialchars($cond) . ' ';
387 $link .= pkg_deplink_annotation($type, $arch, $desc);
388 return $link;
391 $link = htmlspecialchars($name);
392 foreach ($providers as $provider) {
393 if ($provider[1] == $name) {
394 $is_official = ($provider[0] == 0);
395 $name = $provider[1];
396 $link = pkg_provider_link($name, $is_official);
397 break;
400 $link .= htmlspecialchars($cond) . ' ';
402 foreach ($providers as $key => $provider) {
403 if ($provider[1] == $name) {
404 unset($providers[$key]);
408 if (count($providers) > 0) {
409 $link .= '<span class="virtual-dep">(';
410 foreach ($providers as $provider) {
411 $is_official = ($provider[0] == 0);
412 $name = $provider[1];
413 $link .= pkg_provider_link($name, $is_official) . ', ';
415 $link = substr($link, 0, -2);
416 $link .= ')</span>';
419 $link .= pkg_deplink_annotation($type, $arch, $desc);
421 return $link;
425 * Get the HTML code to display a package requirement link
427 * @param string $name The name of the requirement
428 * @param string $depends The (literal) name of the dependency of $name
429 * @param string $type The name of the dependency type
430 * @param string $arch The package dependency architecture
431 * @param string $pkgname The name of dependant package
433 * @return string The HTML code of the link to display
435 function pkg_requiredby_link($name, $depends, $type, $arch, $pkgname) {
436 $link = '<a href="';
437 $link .= htmlspecialchars(get_pkg_uri($name), ENT_QUOTES);
438 $link .= '" title="' . __('View packages details for') .' ' . htmlspecialchars($name) . '">';
439 $link .= htmlspecialchars($name) . '</a>';
441 if ($depends != $pkgname) {
442 $link .= ' <span class="virtual-dep">(';
443 $link .= __('requires %s', htmlspecialchars($depends));
444 $link .= ')</span>';
447 return $link . pkg_deplink_annotation($type, $arch);
451 * Get the HTML code to display a package relation
453 * @param string $name The name of the relation
454 * @param string $cond The package relation condition string
455 * @param string $arch The package relation architecture
457 * @return string The HTML code of the label to display
459 function pkg_rel_html($name, $cond, $arch) {
460 $html = htmlspecialchars($name) . htmlspecialchars($cond);
462 if ($arch) {
463 $html .= ' <em>(' . htmlspecialchars($arch) . ')</em>';
466 return $html;
470 * Get the HTML code to display a source link
472 * @param string $url The URL of the source
473 * @param string $arch The source architecture
474 * @param string $package The name of the package
476 * @return string The HTML code of the label to display
478 function pkg_source_link($url, $arch, $package) {
479 $url = explode('::', $url);
480 $parsed_url = parse_url($url[0]);
482 if (isset($parsed_url['scheme']) || isset($url[1])) {
483 $link = '<a href="' . htmlspecialchars((isset($url[1]) ? $url[1] : $url[0]), ENT_QUOTES) . '">' . htmlspecialchars($url[0]) . '</a>';
484 } else {
485 $file_url = sprintf(config_get('options', 'source_file_uri'), htmlspecialchars($url[0]), $package);
486 $link = '<a href="' . $file_url . '">' . htmlspecialchars($url[0]) . '</a>';
489 if ($arch) {
490 $link .= ' <em>(' . htmlspecialchars($arch) . ')</em>';
493 return $link;
497 * Determine packages that depend on a package
499 * @param string $name The package name for the dependency search
500 * @param array $provides A list of virtual provisions of the package
501 * @param int $limit An upper bound for the number of packages to retrieve
503 * @return array All packages that depend on the specified package name
505 function pkg_required($name="", $provides, $limit) {
506 $deps = array();
507 if ($name != "") {
508 $dbh = DB::connect();
510 $name_list = $dbh->quote($name);
511 foreach ($provides as $p) {
512 $name_list .= ',' . $dbh->quote($p[0]);
515 $q = "SELECT p.Name, pd.DepName, dt.Name, pd.DepArch ";
516 $q.= "FROM PackageDepends pd ";
517 $q.= "LEFT JOIN Packages p ON p.ID = pd.PackageID ";
518 $q.= "LEFT JOIN DependencyTypes dt ON dt.ID = pd.DepTypeID ";
519 $q.= "WHERE pd.DepName IN (" . $name_list . ") ";
520 $q.= "ORDER BY p.Name LIMIT " . intval($limit);
521 $result = $dbh->query($q);
522 if (!$result) {return array();}
523 while ($row = $result->fetch(PDO::FETCH_NUM)) {
524 $deps[] = $row;
527 return $deps;
531 * Get all package sources for a specific package
533 * @param string $pkgid The package ID to get the sources for
535 * @return array All sources associated with a specific package
537 function pkg_sources($pkgid) {
538 $sources = array();
539 $pkgid = intval($pkgid);
540 if ($pkgid > 0) {
541 $dbh = DB::connect();
542 $q = "SELECT Source, SourceArch FROM PackageSources ";
543 $q.= "WHERE PackageID = " . $pkgid;
544 $q.= " ORDER BY Source";
545 $result = $dbh->query($q);
546 if (!$result) {
547 return array();
549 while ($row = $result->fetch(PDO::FETCH_NUM)) {
550 $sources[] = $row;
553 return $sources;
557 * Get the package details
559 * @param string $id The package ID to get description for
561 * @return array The package's details OR error message
563 function pkg_get_details($id=0) {
564 $dbh = DB::connect();
566 $q = "SELECT Packages.*, PackageBases.ID AS BaseID, ";
567 $q.= "PackageBases.Name AS BaseName, PackageBases.NumVotes, ";
568 $q.= "PackageBases.Popularity, PackageBases.OutOfDateTS, ";
569 $q.= "PackageBases.SubmittedTS, PackageBases.ModifiedTS, ";
570 $q.= "PackageBases.SubmitterUID, PackageBases.MaintainerUID, ";
571 $q.= "PackageBases.PackagerUID, PackageBases.FlaggerUID, ";
572 $q.= "(SELECT COUNT(*) FROM PackageRequests ";
573 $q.= " WHERE PackageRequests.PackageBaseID = Packages.PackageBaseID ";
574 $q.= " AND PackageRequests.Status = 0) AS RequestCount ";
575 $q.= "FROM Packages, PackageBases ";
576 $q.= "WHERE PackageBases.ID = Packages.PackageBaseID ";
577 $q.= "AND Packages.ID = " . intval($id);
578 $result = $dbh->query($q);
580 $row = array();
582 if (!$result) {
583 $row['error'] = __("Error retrieving package details.");
585 else {
586 $row = $result->fetch(PDO::FETCH_ASSOC);
587 if (empty($row)) {
588 $row['error'] = __("Package details could not be found.");
592 return $row;
596 * Display the package details page
598 * @param string $id The package ID to get details page for
599 * @param array $row Package details retrieved by pkg_get_details()
600 * @param string $SID The session ID of the visitor
602 * @return void
604 function pkg_display_details($id=0, $row, $SID="") {
605 $dbh = DB::connect();
607 if (isset($row['error'])) {
608 print "<p>" . $row['error'] . "</p>\n";
610 else {
611 $base_id = pkgbase_from_pkgid($id);
612 $pkgbase_name = pkgbase_name_from_id($base_id);
614 include('pkg_details.php');
616 if ($SID) {
617 include('pkg_comment_box.php');
620 $include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED);
622 $limit_pinned = isset($_GET['pinned']) ? 0 : 5;
623 $pinned = pkgbase_comments($base_id, $limit_pinned, false, true);
624 if (!empty($pinned)) {
625 include('pkg_comments.php');
627 unset($pinned);
629 $limit = isset($_GET['comments']) ? 0 : 10;
630 $comments = pkgbase_comments($base_id, $limit, $include_deleted);
631 if (!empty($comments)) {
632 include('pkg_comments.php');
638 * Output the body of the search results page
640 * @param array $params Search parameters
641 * @param bool $show_headers True if statistics should be included
642 * @param string $SID The session ID of the visitor
644 * @return int The total number of packages matching the query
646 function pkg_search_page($params, $show_headers=true, $SID="") {
647 $dbh = DB::connect();
650 * Get commonly used variables.
651 * TODO: Reduce the number of database queries!
653 if ($SID)
654 $myuid = uid_from_sid($SID);
656 /* Sanitize paging variables. */
657 if (isset($params['O'])) {
658 $params['O'] = max(intval($params['O']), 0);
659 } else {
660 $params['O'] = 0;
663 if (isset($params["PP"])) {
664 $params["PP"] = bound(intval($params["PP"]), 50, 250);
665 } else {
666 $params["PP"] = 50;
670 * FIXME: Pull out DB-related code. All of it! This one's worth a
671 * choco-chip cookie, one of those nice big soft ones.
674 /* Build the package search query. */
675 $q_select = "SELECT ";
676 if ($SID) {
677 $q_select .= "PackageNotifications.UserID AS Notify,
678 PackageVotes.UsersID AS Voted, ";
680 $q_select .= "Users.Username AS Maintainer,
681 Packages.Name, Packages.Version, Packages.Description,
682 PackageBases.NumVotes, PackageBases.Popularity, Packages.ID,
683 Packages.PackageBaseID, PackageBases.OutOfDateTS ";
685 $q_from = "FROM Packages
686 LEFT JOIN PackageBases ON (PackageBases.ID = Packages.PackageBaseID)
687 LEFT JOIN Users ON (PackageBases.MaintainerUID = Users.ID) ";
688 if ($SID) {
689 /* This is not needed for the total row count query. */
690 $q_from_extra = "LEFT JOIN PackageVotes
691 ON (PackageBases.ID = PackageVotes.PackageBaseID AND PackageVotes.UsersID = $myuid)
692 LEFT JOIN PackageNotifications
693 ON (PackageBases.ID = PackageNotifications.PackageBaseID AND PackageNotifications.UserID = $myuid) ";
694 } else {
695 $q_from_extra = "";
698 $q_where = 'WHERE PackageBases.PackagerUID IS NOT NULL ';
700 if (isset($params['K'])) {
701 if (isset($params["SeB"]) && $params["SeB"] == "m") {
702 /* Search by maintainer. */
703 $q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . " ";
705 elseif (isset($params["SeB"]) && $params["SeB"] == "c") {
706 /* Search by co-maintainer. */
707 $q_where .= "AND EXISTS (SELECT * FROM PackageComaintainers ";
708 $q_where .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
709 $q_where .= "WHERE PackageComaintainers.PackageBaseID = PackageBases.ID ";
710 $q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . ")";
712 elseif (isset($params["SeB"]) && $params["SeB"] == "M") {
713 /* Search by maintainer and co-maintainer. */
714 $q_where .= "AND (Users.Username = " . $dbh->quote($params['K']) . " ";
715 $q_where .= "OR EXISTS (SELECT * FROM PackageComaintainers ";
716 $q_where .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
717 $q_where .= "WHERE PackageComaintainers.PackageBaseID = PackageBases.ID ";
718 $q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . "))";
720 elseif (isset($params["SeB"]) && $params["SeB"] == "s") {
721 /* Search by submitter. */
722 $q_where .= "AND SubmitterUID = " . intval(uid_from_username($params['K'])) . " ";
724 elseif (isset($params["SeB"]) && $params["SeB"] == "n") {
725 /* Search by name. */
726 $K = "%" . addcslashes($params['K'], '%_') . "%";
727 $q_where .= "AND (Packages.Name LIKE " . $dbh->quote($K) . ") ";
729 elseif (isset($params["SeB"]) && $params["SeB"] == "b") {
730 /* Search by package base name. */
731 $K = "%" . addcslashes($params['K'], '%_') . "%";
732 $q_where .= "AND (PackageBases.Name LIKE " . $dbh->quote($K) . ") ";
734 elseif (isset($params["SeB"]) && $params["SeB"] == "k") {
735 /* Search by keywords. */
736 $q_where .= construct_keyword_search($dbh, $params['K'], false);
738 elseif (isset($params["SeB"]) && $params["SeB"] == "N") {
739 /* Search by name (exact match). */
740 $q_where .= "AND (Packages.Name = " . $dbh->quote($params['K']) . ") ";
742 elseif (isset($params["SeB"]) && $params["SeB"] == "B") {
743 /* Search by package base name (exact match). */
744 $q_where .= "AND (PackageBases.Name = " . $dbh->quote($params['K']) . ") ";
746 else {
747 /* Keyword search (default). */
748 $q_where .= construct_keyword_search($dbh, $params['K'], true);
752 if (isset($params["do_Orphans"])) {
753 $q_where .= "AND MaintainerUID IS NULL ";
756 if (isset($params['outdated'])) {
757 if ($params['outdated'] == 'on') {
758 $q_where .= "AND OutOfDateTS IS NOT NULL ";
760 elseif ($params['outdated'] == 'off') {
761 $q_where .= "AND OutOfDateTS IS NULL ";
765 $order = (isset($params["SO"]) && $params["SO"] == 'd') ? 'DESC' : 'ASC';
767 $q_sort = "ORDER BY ";
768 $sort_by = isset($params["SB"]) ? $params["SB"] : '';
769 switch ($sort_by) {
770 case 'v':
771 $q_sort .= "NumVotes " . $order . ", ";
772 break;
773 case 'p':
774 $q_sort .= "Popularity " . $order . ", ";
775 break;
776 case 'w':
777 if ($SID) {
778 $q_sort .= "Voted " . $order . ", ";
780 break;
781 case 'o':
782 if ($SID) {
783 $q_sort .= "Notify " . $order . ", ";
785 break;
786 case 'm':
787 $q_sort .= "Maintainer " . $order . ", ";
788 break;
789 case 'l':
790 $q_sort .= "ModifiedTS " . $order . ", ";
791 break;
792 case 'a':
793 /* For compatibility with old search links. */
794 $q_sort .= "-ModifiedTS " . $order . ", ";
795 break;
796 default:
797 break;
799 $q_sort .= " Packages.Name " . $order . " ";
801 $q_limit = "LIMIT ".$params["PP"]." OFFSET ".$params["O"];
803 $q = $q_select . $q_from . $q_from_extra . $q_where . $q_sort . $q_limit;
804 $q_total = "SELECT COUNT(*) " . $q_from . $q_where;
806 $result = $dbh->query($q);
807 $result_t = $dbh->query($q_total);
808 if ($result_t) {
809 $row = $result_t->fetch(PDO::FETCH_NUM);
810 $total = $row[0];
812 else {
813 $total = 0;
816 if ($result && $total > 0) {
817 if (isset($params["SO"]) && $params["SO"] == "d"){
818 $SO_next = "a";
820 else {
821 $SO_next = "d";
825 /* Calculate the results to use. */
826 $first = $params['O'] + 1;
828 /* Calculation of pagination links. */
829 $per_page = ($params['PP'] > 0) ? $params['PP'] : 50;
830 $current = ceil($first / $per_page);
831 $pages = ceil($total / $per_page);
832 $templ_pages = array();
834 if ($current > 1) {
835 $templ_pages['&laquo; ' . __('First')] = 0;
836 $templ_pages['&lsaquo; ' . __('Previous')] = ($current - 2) * $per_page;
839 if ($current - 5 > 1)
840 $templ_pages["..."] = false;
842 for ($i = max($current - 5, 1); $i <= min($pages, $current + 5); $i++) {
843 $templ_pages[$i] = ($i - 1) * $per_page;
846 if ($current + 5 < $pages)
847 $templ_pages["... "] = false;
849 if ($current < $pages) {
850 $templ_pages[__('Next') . ' &rsaquo;'] = $current * $per_page;
851 $templ_pages[__('Last') . ' &raquo;'] = ($pages - 1) * $per_page;
854 $searchresults = array();
855 if ($result) {
856 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
857 $searchresults[] = $row;
861 include('pkg_search_results.php');
863 return $total;
867 * Construct the WHERE part of the sophisticated keyword search
869 * @param handle $dbh Database handle
870 * @param string $keywords The search term
871 * @param bool $namedesc Search name and description fields
873 * @return string WHERE part of the SQL clause
875 function construct_keyword_search($dbh, $keywords, $namedesc) {
876 $count = 0;
877 $where_part = "";
878 $q_keywords = "";
879 $op = "";
881 foreach (str_getcsv($keywords, ' ') as $term) {
882 if ($term == "") {
883 continue;
885 if ($count > 0 && strtolower($term) == "and") {
886 $op = "AND ";
887 continue;
889 if ($count > 0 && strtolower($term) == "or") {
890 $op = "OR ";
891 continue;
893 if ($count > 0 && strtolower($term) == "not") {
894 $op .= "NOT ";
895 continue;
898 $term = "%" . addcslashes($term, '%_') . "%";
899 $q_keywords .= $op . " (";
900 if ($namedesc) {
901 $q_keywords .= "Packages.Name LIKE " . $dbh->quote($term) . " OR ";
902 $q_keywords .= "Description LIKE " . $dbh->quote($term) . " OR ";
904 $q_keywords .= "EXISTS (SELECT * FROM PackageKeywords WHERE ";
905 $q_keywords .= "PackageKeywords.PackageBaseID = Packages.PackageBaseID AND ";
906 $q_keywords .= "PackageKeywords.Keyword LIKE " . $dbh->quote($term) . ")) ";
908 $count++;
909 if ($count >= 20) {
910 break;
912 $op = "AND ";
915 if (!empty($q_keywords)) {
916 $where_part = "AND (" . $q_keywords . ") ";
919 return $where_part;
923 * Determine if a POST string has been sent by a visitor
925 * @param string $action String to check has been sent via POST
927 * @return bool True if the POST string was used, otherwise false
929 function current_action($action) {
930 return (isset($_POST['action']) && $_POST['action'] == $action) ||
931 isset($_POST[$action]);
935 * Determine if sent IDs are valid integers
937 * @param array $ids IDs to validate
939 * @return array All sent IDs that are valid integers
941 function sanitize_ids($ids) {
942 $new_ids = array();
943 foreach ($ids as $id) {
944 $id = intval($id);
945 if ($id > 0) {
946 $new_ids[] = $id;
949 return $new_ids;
953 * Determine package information for latest package
955 * @param int $numpkgs Number of packages to get information on
957 * @return array $packages Package info for the specified number of recent packages
959 function latest_pkgs($numpkgs) {
960 $dbh = DB::connect();
962 $q = "SELECT Packages.*, MaintainerUID, SubmittedTS ";
963 $q.= "FROM Packages LEFT JOIN PackageBases ON ";
964 $q.= "PackageBases.ID = Packages.PackageBaseID ";
965 $q.= "ORDER BY SubmittedTS DESC ";
966 $q.= "LIMIT " . intval($numpkgs);
967 $result = $dbh->query($q);
969 $packages = array();
970 if ($result) {
971 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
972 $packages[] = $row;
976 return $packages;