pkgfuncs.inc.php 26.8 KB
Newer Older
1
<?php
2

3
include_once("pkgbasefuncs.inc.php");
eric's avatar
eric committed
4

5
6
7
8
9
10
11
12
13
14
/**
 * Determine if the user can delete a specific package comment
 *
 * Only the comment submitter, Trusted Users, and Developers can delete
 * comments. This function is used for the backend side of comment deletion.
 *
 * @param string $comment_id The comment ID in the database
 *
 * @return bool True if the user can delete the comment, otherwise false
 */
Lukas Fleischer's avatar
Lukas Fleischer committed
15
function can_delete_comment($comment_id=0) {
16
	$dbh = DB::connect();
Lukas Fleischer's avatar
Lukas Fleischer committed
17

18
19
	$q = "SELECT UsersID FROM PackageComments ";
	$q.= "WHERE ID = " . intval($comment_id);
canyonknight's avatar
canyonknight committed
20
	$result = $dbh->query($q);
Lukas Fleischer's avatar
Lukas Fleischer committed
21
22
23

	if (!$result) {
		return false;
24
	}
Lukas Fleischer's avatar
Lukas Fleischer committed
25

26
27
28
	$uid = $result->fetch(PDO::FETCH_COLUMN, 0);

	return has_credential(CRED_COMMENT_DELETE, array($uid));
29
}
eric's avatar
eric committed
30

31
32
33
34
35
36
37
38
39
40
/**
 * Determine if the user can delete a specific package comment using an array
 *
 * Only the comment submitter, Trusted Users, and Developers can delete
 * comments. This function is used for the frontend side of comment deletion.
 *
 * @param array $comment All database information relating a specific comment
 *
 * @return bool True if the user can delete the comment, otherwise false
 */
Lukas Fleischer's avatar
Lukas Fleischer committed
41
42
function can_delete_comment_array($comment) {
	return has_credential(CRED_COMMENT_DELETE, array($comment['UsersID']));
43
44
}

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
 * Determine if the user can edit a specific package comment
 *
 * Only the comment submitter, Trusted Users, and Developers can edit
 * comments. This function is used for the backend side of comment editing.
 *
 * @param string $comment_id The comment ID in the database
 *
 * @return bool True if the user can edit the comment, otherwise false
 */
function can_edit_comment($comment_id=0) {
	$dbh = DB::connect();

	$q = "SELECT UsersID FROM PackageComments ";
	$q.= "WHERE ID = " . intval($comment_id);
	$result = $dbh->query($q);

	if (!$result) {
		return false;
	}

	$uid = $result->fetch(PDO::FETCH_COLUMN, 0);

	return has_credential(CRED_COMMENT_EDIT, array($uid));
}

Marcel Korpel's avatar
Marcel Korpel committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
 * Determine if the user can edit a specific package comment using an array
 *
 * Only the comment submitter, Trusted Users, and Developers can edit
 * comments. This function is used for the frontend side of comment editing.
 *
 * @param array $comment All database information relating a specific comment
 *
 * @return bool True if the user can edit the comment, otherwise false
 */
function can_edit_comment_array($comment) {
	return has_credential(CRED_COMMENT_EDIT, array($comment['UsersID']));
}

85
86
87
/**
 * Determine if the user can pin a specific package comment
 *
88
89
90
 * Only the Package Maintainer, Package Co-maintainers, Trusted Users, and
 * Developers can pin comments. This function is used for the backend side of
 * comment pinning.
91
92
93
94
95
96
97
98
99
100
 *
 * @param string $comment_id The comment ID in the database
 *
 * @return bool True if the user can pin the comment, otherwise false
 */
function can_pin_comment($comment_id=0) {
	$dbh = DB::connect();

	$q = "SELECT MaintainerUID FROM PackageBases AS pb ";
	$q.= "LEFT JOIN PackageComments AS pc ON pb.ID = pc.PackageBaseID ";
101
102
	$q.= "WHERE pc.ID = " . intval($comment_id) . " ";
	$q.= "UNION ";
103
	$q.= "SELECT pcm.UsersID FROM PackageComaintainers AS pcm ";
104
105
	$q.= "LEFT JOIN PackageComments AS pc ";
	$q.= "ON pcm.PackageBaseID = pc.PackageBaseID ";
106
107
108
109
110
111
112
	$q.= "WHERE pc.ID = " . intval($comment_id);
	$result = $dbh->query($q);

	if (!$result) {
		return false;
	}

113
	$uids = $result->fetchAll(PDO::FETCH_COLUMN, 0);
114

115
	return has_credential(CRED_COMMENT_PIN, $uids);
116
117
118
119
120
}

/**
 * Determine if the user can edit a specific package comment using an array
 *
121
122
123
 * Only the Package Maintainer, Package Co-maintainers, Trusted Users, and
 * Developers can pin comments. This function is used for the frontend side of
 * comment pinning.
124
125
126
127
128
129
130
131
132
 *
 * @param array $comment All database information relating a specific comment
 *
 * @return bool True if the user can edit the comment, otherwise false
 */
function can_pin_comment_array($comment) {
	return can_pin_comment($comment['ID']);
}

133
134
135
136
137
138
139
/**
 * Check to see if the package name already exists in the database
 *
 * @param string $name The package name to check
 *
 * @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 ";
canyonknight's avatar
canyonknight committed
144
145
146
147
148
149
	$q.= "WHERE Name = " . $dbh->quote($name);
	$result = $dbh->query($q);
	if (!$result) {
		return;
	}
	$row = $result->fetch(PDO::FETCH_NUM);
150
151
152
	return $row[0];
}

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
 * Get licenses for a specific package
 *
 * @param int $pkgid The package to get licenses for
 *
 * @return array All licenses for the package
 */
function pkg_licenses($pkgid) {
	$lics = array();
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
		$dbh = DB::connect();
		$q = "SELECT l.Name FROM Licenses l ";
		$q.= "INNER JOIN PackageLicenses pl ON pl.LicenseID = l.ID ";
		$q.= "WHERE pl.PackageID = ". $pkgid;
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_COLUMN, 0)) {
			$lics[] = $row;
		}
	}
	return $lics;
}

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
 * Get package groups for a specific package
 *
 * @param int $pkgid The package to get groups for
 *
 * @return array All package groups for the package
 */
function pkg_groups($pkgid) {
	$grps = array();
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
		$dbh = DB::connect();
		$q = "SELECT g.Name FROM Groups g ";
		$q.= "INNER JOIN PackageGroups pg ON pg.GroupID = g.ID ";
		$q.= "WHERE pg.PackageID = ". $pkgid;
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_COLUMN, 0)) {
			$grps[] = $row;
		}
	}
	return $grps;
}

205
206
207
208
209
210
211
212
213
214
/**
 * Get providers for a specific package
 *
 * @param string $name The name of the "package" to get providers for
 *
 * @return array The IDs and names of all providers of the package
 */
function pkg_providers($name) {
	$dbh = DB::connect();
	$q = "SELECT p.ID, p.Name FROM Packages p ";
215
216
217
	$q.= "WHERE p.Name = " . $dbh->quote($name) . " ";
	$q.= "UNION ";
	$q.= "SELECT p.ID, p.Name FROM Packages p ";
218
219
	$q.= "LEFT JOIN PackageRelations pr ON pr.PackageID = p.ID ";
	$q.= "LEFT JOIN RelationTypes rt ON rt.ID = pr.RelTypeID ";
220
	$q.= "WHERE (rt.Name = 'provides' ";
221
	$q.= "AND pr.RelName = " . $dbh->quote($name) . ")";
222
223
224
	$q.= "UNION ";
	$q.= "SELECT 0, Name FROM OfficialProviders ";
	$q.= "WHERE Provides = " . $dbh->quote($name);
225
226
227
228
229
230
231
232
233
234
235
236
237
	$result = $dbh->query($q);

	if (!$result) {
		return array();
	}

	$providers = array();
	while ($row = $result->fetch(PDO::FETCH_NUM)) {
		$providers[] = $row;
	}
	return $providers;
}

238
239
240
241
/**
 * Get package dependencies for a specific package
 *
 * @param int $pkgid The package to get dependencies for
242
 * @param int $limit An upper bound for the number of packages to retrieve
243
244
245
 *
 * @return array All package dependencies for the package
 */
246
function pkg_dependencies($pkgid, $limit) {
247
	$deps = array();
248
249
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
250
		$dbh = DB::connect();
251
252
253
		$q = "SELECT pd.DepName, dt.Name, pd.DepDesc, ";
		$q.= "pd.DepCondition, pd.DepArch, p.ID ";
		$q.= "FROM PackageDepends pd ";
Dan McGee's avatar
Dan McGee committed
254
		$q.= "LEFT JOIN Packages p ON pd.DepName = p.Name ";
255
		$q.= "LEFT JOIN DependencyTypes dt ON dt.ID = pd.DepTypeID ";
Dan McGee's avatar
Dan McGee committed
256
		$q.= "WHERE pd.PackageID = ". $pkgid . " ";
257
		$q.= "ORDER BY pd.DepName LIMIT " . intval($limit);
canyonknight's avatar
canyonknight committed
258
259
260
261
262
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
263
264
265
266
267
268
			$deps[] = $row;
		}
	}
	return $deps;
}

269
270
271
272
273
274
275
276
277
278
279
280
/**
 * Get package relations for a specific package
 *
 * @param int $pkgid The package to get relations for
 *
 * @return array All package relations for the package
 */
function pkg_relations($pkgid) {
	$rels = array();
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
		$dbh = DB::connect();
281
		$q = "SELECT pr.RelName, rt.Name, pr.RelCondition, pr.RelArch, p.ID FROM PackageRelations pr ";
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
		$q.= "LEFT JOIN Packages p ON pr.RelName = p.Name ";
		$q.= "LEFT JOIN RelationTypes rt ON rt.ID = pr.RelTypeID ";
		$q.= "WHERE pr.PackageID = ". $pkgid . " ";
		$q.= "ORDER BY pr.RelName";
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
			$rels[] = $row;
		}
	}
	return $rels;
}

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/**
 * Get the HTML code to display a package dependency link annotation
 * (dependency type, architecture, ...)
 *
 * @param string $type The name of the dependency type
 * @param string $arch The package dependency architecture
 * @param string $desc An optdepends description
 *
 * @return string The HTML code of the label to display
 */
function pkg_deplink_annotation($type, $arch, $desc=false) {
	if ($type == 'depends' && !$arch) {
		return '';
	}

	$link = ' <em>(';

	if ($type == 'makedepends') {
		$link .= 'make';
	} elseif ($type == 'checkdepends') {
		$link .= 'check';
	} elseif ($type == 'optdepends') {
		$link .= 'optional';
	}

	if ($type != 'depends' && $arch) {
		$link .= ', ';
	}

	if ($arch) {
		$link .= htmlspecialchars($arch);
	}

	$link .= ')';
	if ($type == 'optdepends' && $desc) {
		$link .= ' &ndash; ' . htmlspecialchars($desc) . ' </em>';
	}
	$link .= '</em>';

	return $link;
}

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/**
 * Get the HTML code to display a package provider link
 *
 * @param string $name The name of the provider
 * @param bool $official True if the package is in the official repositories
 *
 * @return string The HTML code of the link to display
 */
function pkg_provider_link($name, $official) {
	$link = '<a href="';
	if ($official) {
		$link .= 'https://www.archlinux.org/packages/?q=' .
			urlencode($name);
	} else {
		$link .= htmlspecialchars(get_pkg_uri($name), ENT_QUOTES);
	}
	$link .= '" title="' . __('View packages details for') . ' ';
	$link .= htmlspecialchars($name) . '">';
	$link .= htmlspecialchars($name) . '</a>';

	return $link;
}

362
363
364
365
366
/**
 * Get the HTML code to display a package dependency link
 *
 * @param string $name The name of the dependency
 * @param string $type The name of the dependency type
367
 * @param string $desc The (optional) description of the dependency
368
 * @param string $cond The package dependency condition string
369
 * @param string $arch The package dependency architecture
370
371
372
373
 * @param int $pkg_id The package of the package to display the dependency for
 *
 * @return string The HTML code of the label to display
 */
374
function pkg_depend_link($name, $type, $desc, $cond, $arch, $pkg_id) {
375
376
377
378
379
380
381
382
	/*
	 * TODO: We currently perform one SQL query per nonexistent package
	 * dependency. It would be much better if we could annotate dependency
	 * data with providers so that we already know whether a dependency is
	 * a "provision name" or a package from the official repositories at
	 * this point.
	 */
	$providers = pkg_providers($name);
383

384
385
386
387
388
389
390
391
392
	if (count($providers) == 0) {
		$link = '<span class="broken">';
		$link .= htmlspecialchars($name);
		$link .= '</span>';
		$link .= htmlspecialchars($cond) . ' ';
		$link .= pkg_deplink_annotation($type, $arch, $desc);
		return $link;
	}

393
394
395
396
397
398
399
400
401
	$link = htmlspecialchars($name);
	foreach ($providers as $provider) {
		if ($provider[1] == $name) {
			$is_official = ($provider[0] == 0);
			$name = $provider[1];
			$link = pkg_provider_link($name, $is_official);
			break;
		}
	}
402
	$link .= htmlspecialchars($cond) . ' ';
403
404
405
406
407
408
409

	foreach ($providers as $key => $provider) {
		if ($provider[1] == $name) {
			unset($providers[$key]);
		}
	}

410
411
412
	if (count($providers) > 0) {
		$link .= '<span class="virtual-dep">(';
		foreach ($providers as $provider) {
413
			$is_official = ($provider[0] == 0);
414
			$name = $provider[1];
415
			$link .= pkg_provider_link($name, $is_official) . ', ';
416
417
418
		}
		$link = substr($link, 0, -2);
		$link .= ')</span>';
419
420
	}

421
422
423
	$link .= pkg_deplink_annotation($type, $arch, $desc);

	return $link;
424
}
425

426
427
428
429
/**
 * Get the HTML code to display a package requirement link
 *
 * @param string $name The name of the requirement
430
 * @param string $depends The (literal) name of the dependency of $name
431
432
 * @param string $type The name of the dependency type
 * @param string $arch The package dependency architecture
433
 * @param string $pkgname The name of dependant package
434
435
436
 *
 * @return string The HTML code of the link to display
 */
437
function pkg_requiredby_link($name, $depends, $type, $arch, $pkgname) {
438
439
440
441
442
	$link = '<a href="';
	$link .= htmlspecialchars(get_pkg_uri($name), ENT_QUOTES);
	$link .= '" title="' . __('View packages details for') .' ' . htmlspecialchars($name) . '">';
	$link .= htmlspecialchars($name) . '</a>';

443
444
	if ($depends != $pkgname) {
		$link .= ' <span class="virtual-dep">(';
445
		$link .= __('requires %s', htmlspecialchars($depends));
446
447
448
		$link .= ')</span>';
	}

449
	return $link . pkg_deplink_annotation($type, $arch);
450
}
451

452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/**
 * Get the HTML code to display a package relation
 *
 * @param string $name The name of the relation
 * @param string $cond The package relation condition string
 * @param string $arch The package relation architecture
 *
 * @return string The HTML code of the label to display
 */
function pkg_rel_html($name, $cond, $arch) {
	$html = htmlspecialchars($name) . htmlspecialchars($cond);

	if ($arch) {
		$html .= ' <em>(' . htmlspecialchars($arch) . ')</em>';
	}

	return $html;
}

471
472
473
474
475
/**
 * Get the HTML code to display a source link
 *
 * @param string $url The URL of the source
 * @param string $arch The source architecture
476
 * @param string $package The name of the package
477
478
479
 *
 * @return string The HTML code of the label to display
 */
480
function pkg_source_link($url, $arch, $package) {
481
482
483
484
485
486
	$url = explode('::', $url);
	$parsed_url = parse_url($url[0]);

	if (isset($parsed_url['scheme']) || isset($url[1])) {
		$link = '<a href="' .  htmlspecialchars((isset($url[1]) ? $url[1] : $url[0]), ENT_QUOTES) . '">' . htmlspecialchars($url[0]) . '</a>';
	} else {
487
488
		$file_url = sprintf(config_get('options', 'source_file_uri'), htmlspecialchars($url[0]), $package);
		$link = '<a href="' . $file_url . '">' . htmlspecialchars($url[0]) . '</a>';
489
490
491
492
493
494
495
496
497
	}

	if ($arch) {
		$link .= ' <em>(' . htmlspecialchars($arch) . ')</em>';
	}

	return $link;
}

498
499
500
501
/**
 * Determine packages that depend on a package
 *
 * @param string $name The package name for the dependency search
502
 * @param array $provides A list of virtual provisions of the package
503
 * @param int $limit An upper bound for the number of packages to retrieve
504
505
506
 *
 * @return array All packages that depend on the specified package name
 */
507
function pkg_required($name="", $provides, $limit) {
508
	$deps = array();
Dan McGee's avatar
Dan McGee committed
509
	if ($name != "") {
510
		$dbh = DB::connect();
511
512
513
514
515
516

		$name_list = $dbh->quote($name);
		foreach ($provides as $p) {
			$name_list .= ',' . $dbh->quote($p[0]);
		}

517
518
		$q = "SELECT p.Name, pd.DepName, dt.Name, pd.DepArch ";
		$q.= "FROM PackageDepends pd ";
519
520
		$q.= "LEFT JOIN Packages p ON p.ID = pd.PackageID ";
		$q.= "LEFT JOIN DependencyTypes dt ON dt.ID = pd.DepTypeID ";
521
		$q.= "WHERE pd.DepName IN (" . $name_list . ") ";
522
		$q.= "ORDER BY p.Name LIMIT " . intval($limit);
canyonknight's avatar
canyonknight committed
523
		$result = $dbh->query($q);
524
		if (!$result) {return array();}
canyonknight's avatar
canyonknight committed
525
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
526
527
528
529
530
531
			$deps[] = $row;
		}
	}
	return $deps;
}

532
533
534
535
536
537
538
/**
 * Get all package sources for a specific package
 *
 * @param string $pkgid The package ID to get the sources for
 *
 * @return array All sources associated with a specific package
 */
539
function pkg_sources($pkgid) {
540
	$sources = array();
541
542
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
543
		$dbh = DB::connect();
544
		$q = "SELECT Source, SourceArch FROM PackageSources ";
545
		$q.= "WHERE PackageID = " . $pkgid;
546
		$q.= " ORDER BY Source";
canyonknight's avatar
canyonknight committed
547
548
549
550
551
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
552
			$sources[] = $row;
553
554
555
556
557
		}
	}
	return $sources;
}

558
/**
559
 * Get the package details
560
 *
561
 * @param string $id The package ID to get description for
562
 *
563
564
 * @return array The package's details OR error message
 **/
565
function pkg_get_details($id=0) {
566
	$dbh = DB::connect();
567

568
	$q = "SELECT Packages.*, PackageBases.ID AS BaseID, ";
569
570
	$q.= "PackageBases.Name AS BaseName, PackageBases.NumVotes, ";
	$q.= "PackageBases.Popularity, PackageBases.OutOfDateTS, ";
571
572
	$q.= "PackageBases.SubmittedTS, PackageBases.ModifiedTS, ";
	$q.= "PackageBases.SubmitterUID, PackageBases.MaintainerUID, ";
573
	$q.= "PackageBases.PackagerUID, PackageBases.FlaggerUID, ";
574
575
576
	$q.= "(SELECT COUNT(*) FROM PackageRequests ";
	$q.= " WHERE PackageRequests.PackageBaseID = Packages.PackageBaseID ";
	$q.= " AND PackageRequests.Status = 0) AS RequestCount ";
577
	$q.= "FROM Packages, PackageBases ";
578
	$q.= "WHERE PackageBases.ID = Packages.PackageBaseID ";
Loui Chang's avatar
Loui Chang committed
579
	$q.= "AND Packages.ID = " . intval($id);
canyonknight's avatar
canyonknight committed
580
	$result = $dbh->query($q);
eric's avatar
eric committed
581

582
583
	$row = array();

canyonknight's avatar
canyonknight committed
584
	if (!$result) {
585
		$row['error'] = __("Error retrieving package details.");
586
587
	}
	else {
canyonknight's avatar
canyonknight committed
588
		$row = $result->fetch(PDO::FETCH_ASSOC);
eric's avatar
eric committed
589
		if (empty($row)) {
590
			$row['error'] = __("Package details could not be found.");
591
		}
592
593
594
595
596
597
598
599
600
	}

	return $row;
}

/**
 * Display the package details page
 *
 * @param string $id The package ID to get details page for
601
 * @param array $row Package details retrieved by pkg_get_details()
602
603
604
605
 * @param string $SID The session ID of the visitor
 *
 * @return void
 */
606
function pkg_display_details($id=0, $row, $SID="") {
607
	$dbh = DB::connect();
608

609
610
611
612
	if (isset($row['error'])) {
		print "<p>" . $row['error'] . "</p>\n";
	}
	else {
613
614
615
		$base_id = pkgbase_from_pkgid($id);
		$pkgbase_name = pkgbase_name_from_id($base_id);

616
617
618
		include('pkg_details.php');

		if ($SID) {
619
			include('pkg_comment_box.php');
620
621
		}

622
		$include_deleted = has_credential(CRED_COMMENT_VIEW_DELETED);
623
624
625
626

		$limit_pinned = isset($_GET['pinned']) ? 0 : 5;
		$pinned = pkgbase_comments($base_id, $limit_pinned, false, true);
		if (!empty($pinned)) {
627
			$comment_section = "package";
628
629
			include('pkg_comments.php');
		}
630
		unset($pinned);
631

632

633
634
		$total_comment_count = pkgbase_comments_count($base_id, $include_deleted);
		list($pagination_templs, $per_page, $offset) = calculate_pagination($total_comment_count);
635

636
		$comments = pkgbase_comments($base_id, $per_page, $include_deleted, false, $offset);
637
		if (!empty($comments)) {
638
			$comment_section = "package";
639
			include('pkg_comments.php');
eric's avatar
eric committed
640
641
642
643
		}
	}
}

Lukas Fleischer's avatar
Lukas Fleischer committed
644
645
646
647
648
649
650
/**
 * Output the body of the search results page
 *
 * @param array $params Search parameters
 * @param bool $show_headers True if statistics should be included
 * @param string $SID The session ID of the visitor
 *
651
 * @return int The total number of packages matching the query
Simo Leone's avatar
Simo Leone committed
652
 */
Lukas Fleischer's avatar
Lukas Fleischer committed
653
function pkg_search_page($params, $show_headers=true, $SID="") {
654
	$dbh = DB::connect();
Loui Chang's avatar
Loui Chang committed
655

656
657
658
659
	/*
	 * Get commonly used variables.
	 * TODO: Reduce the number of database queries!
	 */
Loui Chang's avatar
Loui Chang committed
660
	if ($SID)
661
		$myuid = uid_from_sid($SID);
Loui Chang's avatar
Loui Chang committed
662

663
	/* Sanitize paging variables. */
Lukas Fleischer's avatar
Lukas Fleischer committed
664
665
	if (isset($params['O'])) {
		$params['O'] = max(intval($params['O']), 0);
666
	} else {
Lukas Fleischer's avatar
Lukas Fleischer committed
667
		$params['O'] = 0;
Loui Chang's avatar
Loui Chang committed
668
669
	}

Lukas Fleischer's avatar
Lukas Fleischer committed
670
671
	if (isset($params["PP"])) {
		$params["PP"] = bound(intval($params["PP"]), 50, 250);
672
	} else {
Lukas Fleischer's avatar
Lukas Fleischer committed
673
		$params["PP"] = 50;
Loui Chang's avatar
Loui Chang committed
674
675
	}

676
677
678
679
	/*
	 * FIXME: Pull out DB-related code. All of it! This one's worth a
	 * choco-chip cookie, one of those nice big soft ones.
	 */
Loui Chang's avatar
Loui Chang committed
680

681
	/* Build the package search query. */
682
	$q_select = "SELECT ";
Loui Chang's avatar
Loui Chang committed
683
	if ($SID) {
684
		$q_select .= "PackageNotifications.UserID AS Notify,
685
			   PackageVotes.UsersID AS Voted, ";
Loui Chang's avatar
Loui Chang committed
686
	}
687
	$q_select .= "Users.Username AS Maintainer,
688
	Packages.Name, Packages.Version, Packages.Description,
689
690
	PackageBases.NumVotes, PackageBases.Popularity, Packages.ID,
	Packages.PackageBaseID, PackageBases.OutOfDateTS ";
Loui Chang's avatar
Loui Chang committed
691

692
	$q_from = "FROM Packages
693
	LEFT JOIN PackageBases ON (PackageBases.ID = Packages.PackageBaseID)
694
	LEFT JOIN Users ON (PackageBases.MaintainerUID = Users.ID) ";
Loui Chang's avatar
Loui Chang committed
695
	if ($SID) {
696
		/* This is not needed for the total row count query. */
697
		$q_from_extra = "LEFT JOIN PackageVotes
698
		ON (PackageBases.ID = PackageVotes.PackageBaseID AND PackageVotes.UsersID = $myuid)
699
700
		LEFT JOIN PackageNotifications
		ON (PackageBases.ID = PackageNotifications.PackageBaseID AND PackageNotifications.UserID = $myuid) ";
701
702
	} else {
		$q_from_extra = "";
Loui Chang's avatar
Loui Chang committed
703
704
	}

705
	$q_where = 'WHERE PackageBases.PackagerUID IS NOT NULL ';
Loui Chang's avatar
Loui Chang committed
706

Lukas Fleischer's avatar
Lukas Fleischer committed
707
708
	if (isset($params['K'])) {
		if (isset($params["SeB"]) && $params["SeB"] == "m") {
709
			/* Search by maintainer. */
Lukas Fleischer's avatar
Lukas Fleischer committed
710
			$q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . " ";
Loui Chang's avatar
Loui Chang committed
711
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
712
		elseif (isset($params["SeB"]) && $params["SeB"] == "c") {
713
714
715
716
			/* Search by co-maintainer. */
			$q_where .= "AND EXISTS (SELECT * FROM PackageComaintainers ";
			$q_where .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
			$q_where .= "WHERE PackageComaintainers.PackageBaseID = PackageBases.ID ";
Lukas Fleischer's avatar
Lukas Fleischer committed
717
			$q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . ")";
718
		}
719
720
721
722
723
724
725
726
		elseif (isset($params["SeB"]) && $params["SeB"] == "M") {
			/* Search by maintainer and co-maintainer. */
			$q_where .= "AND (Users.Username = " . $dbh->quote($params['K']) . " ";
			$q_where .= "OR EXISTS (SELECT * FROM PackageComaintainers ";
			$q_where .= "INNER JOIN Users ON Users.ID = PackageComaintainers.UsersID ";
			$q_where .= "WHERE PackageComaintainers.PackageBaseID = PackageBases.ID ";
			$q_where .= "AND Users.Username = " . $dbh->quote($params['K']) . "))";
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
727
		elseif (isset($params["SeB"]) && $params["SeB"] == "s") {
728
			/* Search by submitter. */
Lukas Fleischer's avatar
Lukas Fleischer committed
729
			$q_where .= "AND SubmitterUID = " . intval(uid_from_username($params['K'])) . " ";
Loui Chang's avatar
Loui Chang committed
730
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
731
		elseif (isset($params["SeB"]) && $params["SeB"] == "n") {
732
			/* Search by name. */
Lukas Fleischer's avatar
Lukas Fleischer committed
733
			$K = "%" . addcslashes($params['K'], '%_') . "%";
734
			$q_where .= "AND (Packages.Name LIKE " . $dbh->quote($K) . ") ";
Andrea Scarpino's avatar
Andrea Scarpino committed
735
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
736
		elseif (isset($params["SeB"]) && $params["SeB"] == "b") {
737
			/* Search by package base name. */
Lukas Fleischer's avatar
Lukas Fleischer committed
738
			$K = "%" . addcslashes($params['K'], '%_') . "%";
739
740
			$q_where .= "AND (PackageBases.Name LIKE " . $dbh->quote($K) . ") ";
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
741
		elseif (isset($params["SeB"]) && $params["SeB"] == "k") {
Marcel Korpel's avatar
Marcel Korpel committed
742
			/* Search by keywords. */
Lukas Fleischer's avatar
Lukas Fleischer committed
743
			$q_where .= construct_keyword_search($dbh, $params['K'], false);
Marcel Korpel's avatar
Marcel Korpel committed
744
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
745
		elseif (isset($params["SeB"]) && $params["SeB"] == "N") {
746
			/* Search by name (exact match). */
Lukas Fleischer's avatar
Lukas Fleischer committed
747
			$q_where .= "AND (Packages.Name = " . $dbh->quote($params['K']) . ") ";
748
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
749
		elseif (isset($params["SeB"]) && $params["SeB"] == "B") {
750
			/* Search by package base name (exact match). */
Lukas Fleischer's avatar
Lukas Fleischer committed
751
			$q_where .= "AND (PackageBases.Name = " . $dbh->quote($params['K']) . ") ";
752
		}
Loui Chang's avatar
Loui Chang committed
753
		else {
754
			/* Keyword search (default). */
Lukas Fleischer's avatar
Lukas Fleischer committed
755
			$q_where .= construct_keyword_search($dbh, $params['K'], true);
Loui Chang's avatar
Loui Chang committed
756
757
758
		}
	}

Lukas Fleischer's avatar
Lukas Fleischer committed
759
	if (isset($params["do_Orphans"])) {
760
		$q_where .= "AND MaintainerUID IS NULL ";
Loui Chang's avatar
Loui Chang committed
761
	}
762

Lukas Fleischer's avatar
Lukas Fleischer committed
763
764
	if (isset($params['outdated'])) {
		if ($params['outdated'] == 'on') {
765
			$q_where .= "AND OutOfDateTS IS NOT NULL ";
766
		}
Lukas Fleischer's avatar
Lukas Fleischer committed
767
		elseif ($params['outdated'] == 'off') {
768
			$q_where .= "AND OutOfDateTS IS NULL ";
769
		}
770
771
	}

Lukas Fleischer's avatar
Lukas Fleischer committed
772
	$order = (isset($params["SO"]) && $params["SO"] == 'd') ? 'DESC' : 'ASC';
Loui Chang's avatar
Loui Chang committed
773

774
	$q_sort = "ORDER BY ";
Lukas Fleischer's avatar
Lukas Fleischer committed
775
	$sort_by = isset($params["SB"]) ? $params["SB"] : '';
776
	switch ($sort_by) {
Loui Chang's avatar
Loui Chang committed
777
	case 'v':
778
		$q_sort .= "NumVotes " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
779
		break;
780
781
782
	case 'p':
		$q_sort .= "Popularity " . $order . ", ";
		break;
783
784
	case 'w':
		if ($SID) {
785
			$q_sort .= "Voted " . $order . ", ";
786
787
788
789
		}
		break;
	case 'o':
		if ($SID) {
790
			$q_sort .= "Notify " . $order . ", ";
791
792
		}
		break;
Loui Chang's avatar
Loui Chang committed
793
	case 'm':
794
		$q_sort .= "Maintainer " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
795
		break;
796
797
798
	case 'l':
		$q_sort .= "ModifiedTS " . $order . ", ";
		break;
Loui Chang's avatar
Loui Chang committed
799
	case 'a':
800
		/* For compatibility with old search links. */
801
		$q_sort .= "-ModifiedTS " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
802
803
804
805
		break;
	default:
		break;
	}
806
	$q_sort .= " Packages.Name " . $order . " ";
Loui Chang's avatar
Loui Chang committed
807

Lukas Fleischer's avatar
Lukas Fleischer committed
808
	$q_limit = "LIMIT ".$params["PP"]." OFFSET ".$params["O"];
809

810
811
	$q = $q_select . $q_from . $q_from_extra . $q_where . $q_sort . $q_limit;
	$q_total = "SELECT COUNT(*) " . $q_from . $q_where;
Loui Chang's avatar
Loui Chang committed
812

canyonknight's avatar
canyonknight committed
813
814
	$result = $dbh->query($q);
	$result_t = $dbh->query($q_total);
815
	if ($result_t) {
canyonknight's avatar
canyonknight committed
816
817
		$row = $result_t->fetch(PDO::FETCH_NUM);
		$total = $row[0];
818
819
820
821
	}
	else {
		$total = 0;
	}
Simo Leone's avatar
Simo Leone committed
822

823
	if ($result && $total > 0) {
Lukas Fleischer's avatar
Lukas Fleischer committed
824
		if (isset($params["SO"]) && $params["SO"] == "d"){
825
			$SO_next = "a";
Loui Chang's avatar
Loui Chang committed
826
827
		}
		else {
828
			$SO_next = "d";
Loui Chang's avatar
Loui Chang committed
829
		}
830
	}
Simo Leone's avatar
Simo Leone committed
831

832
	/* Calculate the results to use. */
Lukas Fleischer's avatar
Lukas Fleischer committed
833
	$first = $params['O'] + 1;
Simo Leone's avatar
Simo Leone committed
834

835
	/* Calculation of pagination links. */
Lukas Fleischer's avatar
Lukas Fleischer committed
836
	$per_page = ($params['PP'] > 0) ? $params['PP'] : 50;
837
838
839
	$current = ceil($first / $per_page);
	$pages = ceil($total / $per_page);
	$templ_pages = array();
840

841
	if ($current > 1) {
842
843
		$templ_pages['&laquo; ' . __('First')] = 0;
		$templ_pages['&lsaquo; ' . __('Previous')] = ($current - 2) * $per_page;
844
	}
845

846
847
	if ($current - 5 > 1)
		$templ_pages["..."] = false;
848

849
850
851
	for ($i = max($current - 5, 1); $i <= min($pages, $current + 5); $i++) {
		$templ_pages[$i] = ($i - 1) * $per_page;
	}
852

853
854
	if ($current + 5 < $pages)
		$templ_pages["... "] = false;
855

856
	if ($current < $pages) {
857
858
		$templ_pages[__('Next') . ' &rsaquo;'] = $current * $per_page;
		$templ_pages[__('Last') . ' &raquo;'] = ($pages - 1) * $per_page;
859
	}
Simo Leone's avatar
Simo Leone committed
860

861
	$searchresults = array();
canyonknight's avatar
canyonknight committed
862
863
864
865
	if ($result) {
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
			$searchresults[] = $row;
		}
866
867
	}

868
869
	include('pkg_search_results.php');

870
	return $total;
eric's avatar
eric committed
871
872
}

Marcel Korpel's avatar
Marcel Korpel committed
873
874
875
876
/**
 * Construct the WHERE part of the sophisticated keyword search
 *
 * @param handle $dbh Database handle
Lukas Fleischer's avatar
Lukas Fleischer committed
877
878
 * @param string $keywords The search term
 * @param bool $namedesc Search name and description fields
Marcel Korpel's avatar
Marcel Korpel committed
879
880
881
 *
 * @return string WHERE part of the SQL clause
 */
Lukas Fleischer's avatar
Lukas Fleischer committed
882
function construct_keyword_search($dbh, $keywords, $namedesc) {
Marcel Korpel's avatar
Marcel Korpel committed
883
884
885
886
887
	$count = 0;
	$where_part = "";
	$q_keywords = "";
	$op = "";

Lukas Fleischer's avatar
Lukas Fleischer committed
888
	foreach (str_getcsv($keywords, ' ') as $term) {
Marcel Korpel's avatar
Marcel Korpel committed
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
		if ($term == "") {
			continue;
		}
		if ($count > 0 && strtolower($term) == "and") {
			$op = "AND ";
			continue;
		}
		if ($count > 0 && strtolower($term) == "or") {
			$op = "OR ";
			continue;
		}
	        if ($count > 0 && strtolower($term) == "not") {
			$op .= "NOT ";
			continue;
		}

		$term = "%" . addcslashes($term, '%_') . "%";
		$q_keywords .= $op . " (";
		if ($namedesc) {
			$q_keywords .= "Packages.Name LIKE " . $dbh->quote($term) . " OR ";
			$q_keywords .= "Description LIKE " . $dbh->quote($term) . " OR ";
		}
		$q_keywords .= "EXISTS (SELECT * FROM PackageKeywords WHERE ";
		$q_keywords .= "PackageKeywords.PackageBaseID = Packages.PackageBaseID AND ";
		$q_keywords .= "PackageKeywords.Keyword LIKE " . $dbh->quote($term) . ")) ";

		$count++;
		if ($count >= 20) {
			break;
		}
		$op = "AND ";
	}

	if (!empty($q_keywords)) {
		$where_part = "AND (" . $q_keywords . ") ";
	}

	return $where_part;
}

929
930
931
932
933
934
935
/**
 * Determine if a POST string has been sent by a visitor
 *
 * @param string $action String to check has been sent via POST
 *
 * @return bool True if the POST string was used, otherwise false
 */
Dan McGee's avatar
Dan McGee committed
936
937
938
939
940
function current_action($action) {
	return (isset($_POST['action']) && $_POST['action'] == $action) ||
		isset($_POST[$action]);
}

941
/**
942
943
944
945
946
 * Determine if sent IDs are valid integers
 *
 * @param array $ids IDs to validate
 *
 * @return array All sent IDs that are valid integers
947
948
949
950
951
952
953
954
955
956
957
958
 */
function sanitize_ids($ids) {
	$new_ids = array();
	foreach ($ids as $id) {
		$id = intval($id);
		if ($id > 0) {
			$new_ids[] = $id;
		}
	}
	return $new_ids;
}

959
960
961
962
963
964
965
966
967
968
/**
 * Determine package information for latest package
 *
 * @param int $numpkgs Number of packages to get information on
 *
 * @return array $packages Package info for the specified number of recent packages
 */
function latest_pkgs($numpkgs) {
	$dbh = DB::connect();

Lukas Fleischer's avatar
Lukas Fleischer committed
969
970
	$q = "SELECT Packages.*, MaintainerUID, SubmittedTS ";
	$q.= "FROM Packages LEFT JOIN PackageBases ON ";
971
	$q.= "PackageBases.ID = Packages.PackageBaseID ";
972
	$q.= "ORDER BY SubmittedTS DESC ";
973
	$q.= "LIMIT " . intval($numpkgs);
974
975
	$result = $dbh->query($q);

976
	$packages = array();
977
978
979
980
981
982
983
984
	if ($result) {
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
			$packages[] = $row;
		}
	}

	return $packages;
}