1<?php
2/**
3 * MyBB 1.8
4 * Copyright 2014 MyBB Group, All Rights Reserved
5 *
6 * Website: http://www.mybb.com
7 * License: http://www.mybb.com/about/license
8 *
9 */
10
11// Disallow direct access to this file for security reasons
12if(!defined("IN_MYBB"))
13{
14	die("Direct initialization of this file is not allowed.<br /><br />Please make sure IN_MYBB is defined.");
15}
16
17$page->add_breadcrumb_item($lang->attachments, "index.php?module=forum-attachments");
18
19if($mybb->input['action'] == "stats" || $mybb->input['action'] == "orphans" || !$mybb->input['action'])
20{
21	$sub_tabs['find_attachments'] = array(
22		'title' => $lang->find_attachments,
23		'link' => "index.php?module=forum-attachments",
24		'description' => $lang->find_attachments_desc
25	);
26
27	$sub_tabs['find_orphans'] = array(
28		'title' => $lang->find_orphans,
29		'link' => "index.php?module=forum-attachments&amp;action=orphans",
30		'description' => $lang->find_orphans_desc
31	);
32
33	$sub_tabs['stats'] = array(
34		'title' => $lang->attachment_stats,
35		'link' => "index.php?module=forum-attachments&amp;action=stats",
36		'description' => $lang->attachment_stats_desc
37	);
38}
39
40$plugins->run_hooks("admin_forum_attachments_begin");
41
42$uploadspath_abs = mk_path_abs($mybb->settings['uploadspath']);
43
44$default_perpage = 20;
45$perpage = $mybb->get_input('perpage', MyBB::INPUT_INT);
46if(!$perpage)
47{
48	$perpage = $default_perpage;
49}
50
51if($mybb->input['action'] == "delete")
52{
53	$plugins->run_hooks("admin_forum_attachments_delete");
54
55	if(isset($mybb->input['aids']))
56	{
57		if(!is_array($mybb->input['aids']))
58		{
59			$mybb->input['aids'] = array($mybb->get_input('aid', MyBB::INPUT_INT));
60		}
61		else
62		{
63			$mybb->input['aids'] = array_map("intval", $mybb->input['aids']);
64		}
65	}
66	else
67	{
68		$mybb->input['aids'] = array();
69	}
70
71	if(count($mybb->input['aids']) < 1)
72	{
73		flash_message($lang->error_nothing_selected, 'error');
74		admin_redirect("index.php?module=forum-attachments");
75	}
76
77	if($mybb->request_method == "post")
78	{
79		require_once MYBB_ROOT."inc/functions_upload.php";
80
81		$query = $db->simple_select("attachments", "aid,pid,posthash, filename", "aid IN (".implode(",", $mybb->input['aids']).")");
82		while($attachment = $db->fetch_array($query))
83		{
84			if(!$attachment['pid'])
85			{
86				remove_attachment(null, $attachment['posthash'], $attachment['aid']);
87				// Log admin action
88				log_admin_action($attachment['aid'], $attachment['filename']);
89			}
90			else
91			{
92				remove_attachment($attachment['pid'], null, $attachment['aid']);
93				// Log admin action
94				log_admin_action($attachment['aid'], $attachment['filename'], $attachment['pid']);
95			}
96		}
97
98		$plugins->run_hooks("admin_forum_attachments_delete_commit");
99
100		flash_message($lang->success_deleted, 'success');
101		admin_redirect("index.php?module=forum-attachments");
102	}
103	else
104	{
105		$aids = array();
106		foreach($mybb->input['aids'] as $aid)
107		{
108			$aids .= "&amp;aids[]=$aid";
109		}
110		$page->output_confirm_action("index.php?module=forum-attachments&amp;action=delete&amp;aids={$aids}", $lang->confirm_delete);
111	}
112}
113
114if($mybb->input['action'] == "stats")
115{
116	$plugins->run_hooks("admin_forum_attachments_stats");
117
118	$query = $db->simple_select("attachments", "COUNT(*) AS total_attachments, SUM(filesize) as disk_usage, SUM(downloads*filesize) as bandwidthused", "visible='1'");
119	$attachment_stats = $db->fetch_array($query);
120
121		$page->add_breadcrumb_item($lang->stats);
122		$page->output_header($lang->stats_attachment_stats);
123
124		$page->output_nav_tabs($sub_tabs, 'stats');
125
126	if($attachment_stats['total_attachments'] == 0)
127	{
128		$page->output_inline_error(array($lang->error_no_attachments));
129		$page->output_footer();
130		exit;
131	}
132
133	$table = new Table;
134
135	$table->construct_cell($lang->num_uploaded, array('width' => '25%'));
136	$table->construct_cell(my_number_format($attachment_stats['total_attachments']), array('width' => '25%'));
137	$table->construct_cell($lang->space_used, array('width' => '200'));
138	$table->construct_cell(get_friendly_size($attachment_stats['disk_usage']), array('width' => '200'));
139	$table->construct_row();
140
141	$table->construct_cell($lang->bandwidth_used, array('width' => '25%'));
142	$table->construct_cell(get_friendly_size(round($attachment_stats['bandwidthused'])), array('width' => '25%'));
143	$table->construct_cell($lang->average_size, array('width' => '25%'));
144	$table->construct_cell(get_friendly_size(round($attachment_stats['disk_usage']/$attachment_stats['total_attachments'])), array('width' => '25%'));
145	$table->construct_row();
146
147	$table->output($lang->general_stats);
148
149	// Fetch the most popular attachments
150	$table = new Table;
151	$table->construct_header($lang->attachments, array('colspan' => 2));
152	$table->construct_header($lang->size, array('width' => '10%', 'class' => 'align_center'));
153	$table->construct_header($lang->posted_by, array('width' => '20%', 'class' => 'align_center'));
154	$table->construct_header($lang->thread, array('width' => '25%', 'class' => 'align_center'));
155	$table->construct_header($lang->downloads, array('width' => '10%', 'class' => 'align_center'));
156	$table->construct_header($lang->date_uploaded, array("class" => "align_center"));
157
158	$query = $db->query("
159		SELECT a.*, p.tid, p.fid, t.subject, p.uid, p.username, u.username AS user_username
160		FROM ".TABLE_PREFIX."attachments a
161		LEFT JOIN ".TABLE_PREFIX."posts p ON (p.pid=a.pid)
162		LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
163		LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=a.uid)
164		ORDER BY a.downloads DESC
165		LIMIT 5
166	");
167	while($attachment = $db->fetch_array($query))
168	{
169		build_attachment_row($attachment, $table);
170	}
171	$table->output($lang->popular_attachments);
172
173	// Fetch the largest attachments
174	$table = new Table;
175	$table->construct_header($lang->attachments, array('colspan' => 2));
176	$table->construct_header($lang->size, array('width' => '10%', 'class' => 'align_center'));
177	$table->construct_header($lang->posted_by, array('width' => '20%', 'class' => 'align_center'));
178	$table->construct_header($lang->thread, array('width' => '25%', 'class' => 'align_center'));
179	$table->construct_header($lang->downloads, array('width' => '10%', 'class' => 'align_center'));
180	$table->construct_header($lang->date_uploaded, array("class" => "align_center"));
181
182	$query = $db->query("
183		SELECT a.*, p.tid, p.fid, t.subject, p.uid, p.username, u.username AS user_username
184		FROM ".TABLE_PREFIX."attachments a
185		LEFT JOIN ".TABLE_PREFIX."posts p ON (p.pid=a.pid)
186		LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
187		LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=a.uid)
188		ORDER BY a.filesize DESC
189		LIMIT 5
190	");
191	while($attachment = $db->fetch_array($query))
192	{
193		build_attachment_row($attachment, $table);
194	}
195	$table->output($lang->largest_attachments);
196
197	// Fetch users who've uploaded the most attachments
198	$table = new Table;
199	$table->construct_header($lang->username);
200	$table->construct_header($lang->total_size, array('width' => '20%', 'class' => 'align_center'));
201
202	switch($db->type)
203	{
204		case "pgsql":
205			$query = $db->query("
206				SELECT a.uid, u.username, SUM(a.filesize) as totalsize
207				FROM ".TABLE_PREFIX."attachments a
208				LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=a.uid)
209				GROUP BY a.uid, u.username
210				ORDER BY totalsize DESC
211				LIMIT 5
212			");
213			break;
214		default:
215			$query = $db->query("
216				SELECT a.uid, u.username, SUM(a.filesize) as totalsize
217				FROM ".TABLE_PREFIX."attachments a
218				LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=a.uid)
219				GROUP BY a.uid
220				ORDER BY totalsize DESC
221				LIMIT 5
222			");
223	}
224	while($user = $db->fetch_array($query))
225	{
226		if(!$user['uid'])
227		{
228			$user['username'] = $lang->na;
229		}
230		$table->construct_cell(build_profile_link(htmlspecialchars_uni($user['username']), $user['uid'], "_blank"));
231		$table->construct_cell("<a href=\"index.php?module=forum-attachments&amp;results=1&amp;username=".urlencode($user['username'])."\" target=\"_blank\">".get_friendly_size($user['totalsize'])."</a>", array('class' => 'align_center'));
232		$table->construct_row();
233	}
234	$table->output($lang->users_diskspace);
235
236	$page->output_footer();
237}
238
239if($mybb->input['action'] == "delete_orphans" && $mybb->request_method == "post")
240{
241	$plugins->run_hooks("admin_forum_attachments_delete_orphans");
242
243	$success_count = $error_count = 0;
244
245	// Deleting specific attachments from uploads directory
246	if(is_array($mybb->input['orphaned_files']))
247	{
248		foreach($mybb->input['orphaned_files'] as $file)
249		{
250			$file = str_replace('..', '', $file);
251			$path = $uploadspath_abs."/".$file;
252			$real_path = realpath($path);
253
254			if($real_path === false || strpos(str_replace('\\', '/', $real_path), str_replace('\\', '/', realpath(MYBB_ROOT)).'/') !== 0 || $real_path == realpath(MYBB_ROOT.'install/lock'))
255			{
256				$error_count++;
257				continue;
258			}
259
260			if(!@unlink($uploadspath_abs."/".$file))
261			{
262				$error_count++;
263			}
264			else
265			{
266				$success_count++;
267			}
268		}
269	}
270
271	// Deleting physical attachments which exist in database
272	if(is_array($mybb->input['orphaned_attachments']))
273	{
274		$mybb->input['orphaned_attachments'] = array_map("intval", $mybb->input['orphaned_attachments']);
275		require_once MYBB_ROOT."inc/functions_upload.php";
276
277		$query = $db->simple_select("attachments", "aid,pid,posthash", "aid IN (".implode(",", $mybb->input['orphaned_attachments']).")");
278		while($attachment = $db->fetch_array($query))
279		{
280			if(!$attachment['pid'])
281			{
282				remove_attachment(null, $attachment['posthash'], $attachment['aid']);
283			}
284			else
285			{
286				remove_attachment($attachment['pid'], null, $attachment['aid']);
287			}
288			$success_count++;
289		}
290	}
291
292	$plugins->run_hooks("admin_forum_attachments_delete_orphans_commit");
293
294	// Log admin action
295	log_admin_action();
296
297	$message = '';
298	$status = 'success';
299	if($error_count > 0)
300	{
301		$status = 'error';
302		$message = $lang->sprintf($lang->error_count, $error_count);
303	}
304
305	if($success_count > 0)
306	{
307		if($error_count > 0)
308		{
309			$message .= '<br />'.$lang->sprintf($lang->success_count, $success_count);
310		}
311		else
312		{
313			$message = $lang->success_orphan_deleted;
314		}
315	}
316	flash_message($message, $status);
317	admin_redirect('index.php?module=forum-attachments');
318}
319
320if($mybb->input['action'] == "orphans")
321{
322	$plugins->run_hooks("admin_forum_attachments_orphans");
323
324	// Oprhans are defined as:
325	// - Uploaded files in the uploads directory that don't exist in the database
326	// - Attachments for which the uploaded file is missing
327	// - Attachments for which the thread or post has been deleted
328	// - Files uploaded > 24h ago not attached to a real post
329
330	// This process is quite intensive so we split it up in to 2 steps, one which scans the file system and the other which scans the database.
331
332	$mybb->input['step'] = $mybb->get_input('step', MyBB::INPUT_INT);
333
334	// Finished second step, show results
335	if($mybb->input['step'] == 3)
336	{
337		$plugins->run_hooks("admin_forum_attachments_step3");
338
339		$reults = 0;
340		// Incoming attachments which exist as files but not in database
341		if(!empty($mybb->input['bad_attachments']))
342		{
343			$bad_attachments = my_unserialize($mybb->input['bad_attachments']);
344			$results = count($bad_attachments);
345		}
346
347		$aids = array();
348		if(!empty($mybb->input['missing_attachment_files']))
349		{
350			$missing_attachment_files = my_unserialize($mybb->input['missing_attachment_files']);
351			$aids = array_merge($aids, $missing_attachment_files);
352		}
353
354		if(!empty($mybb->input['missing_threads']))
355		{
356			$missing_threads = my_unserialize($mybb->input['missing_threads']);
357			$aids = array_merge($aids, $missing_threads);
358		}
359
360		if(!empty($mybb->input['incomplete_attachments']))
361		{
362			$incomplete_attachments = my_unserialize($mybb->input['incomplete_attachments']);
363			$aids = array_merge($aids, $incomplete_attachments);
364		}
365
366		foreach($aids as $key => $aid)
367		{
368			$aids[$key] = (int)$aid;
369		}
370
371		$results = count($aids);
372
373		if($results == 0)
374		{
375			flash_message($lang->success_no_orphans, 'success');
376			admin_redirect("index.php?module=forum-attachments");
377		}
378
379		$page->output_header($lang->orphan_results);
380		$page->output_nav_tabs($sub_tabs, 'find_orphans');
381
382		$form = new Form("index.php?module=forum-attachments&amp;action=delete_orphans", "post");
383
384		$table = new Table;
385		$table->construct_header($form->generate_check_box('allbox', '1', '', array('class' => 'checkall')), array( 'width' => 1));
386		$table->construct_header($lang->size_attachments, array('colspan' => 2));
387		$table->construct_header($lang->reason_orphaned, array('width' => '20%', 'class' => 'align_center'));
388		$table->construct_header($lang->date_uploaded, array("class" => "align_center"));
389
390		if(is_array($bad_attachments))
391		{
392			foreach($bad_attachments as $file)
393			{
394				$file_path = $uploadspath_abs."/".$file;
395
396				if(file_exists($file_path))
397				{
398					$filename = htmlspecialchars_uni($file);
399					$filesize = get_friendly_size(filesize($file_path));
400					$table->construct_cell($form->generate_check_box('orphaned_files[]', $file, '', array('checked' => true)));
401					$table->construct_cell(get_attachment_icon(get_extension($attachment['filename'])), array('width' => 1));
402					$table->construct_cell("<span class=\"float_right\">{$filesize}</span>{$filename}");
403					$table->construct_cell($lang->reason_not_in_table, array('class' => 'align_center'));
404					$table->construct_cell(my_date('relative', filemtime($file_path)), array('class' => 'align_center'));
405					$table->construct_row();
406				}
407			}
408		}
409
410		if(count($aids) > 0)
411		{
412			$query = $db->simple_select("attachments", "*", "aid IN (".implode(",", $aids).")");
413			while($attachment = $db->fetch_array($query))
414			{
415				$attachment['filename'] = htmlspecialchars_uni($attachment['filename']);
416
417				if($missing_attachment_files[$attachment['aid']])
418				{
419					$reason = $lang->reason_file_missing;
420				}
421				else if($missing_threads[$attachment['aid']])
422				{
423					$reason = $lang->reason_thread_deleted;
424				}
425				else if($incomplete_attachments[$attachment['aid']])
426				{
427					$reason = $lang->reason_post_never_made;
428				}
429				$table->construct_cell($form->generate_check_box('orphaned_attachments[]', $attachment['aid'], '', array('checked' => true)));
430				$table->construct_cell(get_attachment_icon(get_extension($attachment['filename'])), array('width' => 1));
431				$table->construct_cell("<span class=\"float_right\">".get_friendly_size($attachment['filesize'])."</span>{$attachment['filename']}");
432				$table->construct_cell($reason, array('class' => 'align_center'));
433				if($attachment['dateuploaded'])
434				{
435					$table->construct_cell(my_date('relative', $attachment['dateuploaded']), array('class' => 'align_center'));
436				}
437				else
438				{
439					$table->construct_cell($lang->unknown, array('class' => 'align_center'));
440				}
441				$table->construct_row();
442			}
443		}
444
445		$table->output("{$lang->orphan_attachments_search} - {$results} {$lang->results}");
446
447		$buttons[] = $form->generate_submit_button($lang->button_delete_orphans);
448		$form->output_submit_wrapper($buttons);
449		$form->end();
450		$page->output_footer();
451	}
452
453	// Running second step - scan the database
454	else if($mybb->input['step'] == 2)
455	{
456		$plugins->run_hooks("admin_forum_attachments_orphans_step2");
457
458		$page->output_header("{$lang->orphan_attachments_search} - {$lang->step2}");
459
460		$page->output_nav_tabs($sub_tabs, 'find_orphans');
461		echo "<h3>{$lang->step2of2}</h3>";
462		echo "<p class=\"align_center\">{$lang->step2of2_line1}</p>";
463		echo "<p class=\"align_center\">{$lang->step_line2}</p>";
464		echo "<p class=\"align_center\"><img src=\"styles/{$page->style}/images/spinner_big.gif\" alt=\"{$lang->scanning}\" id=\"spinner\" /></p>";
465
466		$page->output_footer(false);
467		flush();
468
469		$missing_attachment_files = array();
470		$missing_threads = array();
471		$incomplete_attachments = array();
472
473		$query = $db->query("
474			SELECT a.*, a.pid AS attachment_pid, p.pid
475			FROM ".TABLE_PREFIX."attachments a
476			LEFT JOIN ".TABLE_PREFIX."posts p ON (p.pid=a.pid)
477			ORDER BY a.aid");
478		while($attachment = $db->fetch_array($query))
479		{
480			// Check if the attachment exists in the file system
481			if(!file_exists($uploadspath_abs."/{$attachment['attachname']}"))
482			{
483				$missing_attachment_files[$attachment['aid']] = $attachment['aid'];
484			}
485			// Check if the thread/post for this attachment is missing
486			else if(!$attachment['pid'] && $attachment['attachment_pid'])
487			{
488				$missing_threads[$attachment['aid']] = $attachment['aid'];
489			}
490			// Check if the attachment was uploaded > 24 hours ago but not assigned to a thread
491			else if(!$attachment['attachment_pid'] && $attachment['dateuploaded'] < TIME_NOW-60*60*24 && $attachment['dateuploaded'] != 0)
492			{
493				$incomplete_attachments[$attachment['aid']] = $attachment['aid'];
494			}
495		}
496
497		// Now send the user to the final page
498		$form = new Form("index.php?module=forum-attachments&amp;action=orphans&amp;step=3", "post", "redirect_form", 0, "");
499		// Scan complete
500		if($mybb->get_input('bad_attachments'))
501		{
502			echo $form->generate_hidden_field("bad_attachments", $mybb->input['bad_attachments']);
503		}
504		if(is_array($missing_attachment_files) && count($missing_attachment_files) > 0)
505		{
506			$missing_attachment_files = my_serialize($missing_attachment_files);
507			echo $form->generate_hidden_field("missing_attachment_files", $missing_attachment_files);
508		}
509		if(is_array($missing_threads) && count($missing_threads) > 0)
510		{
511			$missing_threads = my_serialize($missing_threads);
512			echo $form->generate_hidden_field("missing_threads", $missing_threads);
513		}
514		if(is_array($incomplete_attachments) && count($incomplete_attachments) > 0)
515		{
516			$incomplete_attachments = my_serialize($incomplete_attachments);
517			echo $form->generate_hidden_field("incomplete_attachments", $incomplete_attachments);
518		}
519		$form->end();
520		echo "<script type=\"text/javascript\">$(function() {
521				window.setTimeout(
522					function() {
523						$(\"#redirect_form\").trigger('submit');
524					}, 100
525				);
526			});</script>";
527		exit;
528	}
529	// Running first step, scan the file system
530	else
531	{
532		$plugins->run_hooks("admin_forum_attachments_orphans_step1");
533
534		/**
535		 * @param string $dir
536		 */
537		function scan_attachments_directory($dir="")
538		{
539			global $db, $mybb, $bad_attachments, $attachments_to_check, $uploadspath_abs;
540
541			$real_dir = $uploadspath_abs;
542			$false_dir = "";
543			if($dir)
544			{
545				$real_dir .= "/".$dir;
546				$false_dir = $dir."/";
547			}
548
549			if($dh = opendir($real_dir))
550			{
551				while(false !== ($file = readdir($dh)))
552				{
553					if($file == "." || $file == ".." || $file == ".svn")
554					{
555						continue;
556					}
557
558					if(is_dir($real_dir.'/'.$file))
559					{
560						scan_attachments_directory($false_dir.$file);
561					}
562					else if(my_substr($file, -7, 7) == ".attach")
563					{
564						$attachments_to_check["$false_dir$file"] = $false_dir.$file;
565						// In allotments of 20, query the database for these attachments
566						if(count($attachments_to_check) >= 20)
567						{
568							$attachments_to_check = array_map(array($db, "escape_string"), $attachments_to_check);
569							$attachment_names = "'".implode("','", $attachments_to_check)."'";
570							$query = $db->simple_select("attachments", "aid, attachname", "attachname IN ($attachment_names)");
571							while($attachment = $db->fetch_array($query))
572							{
573								unset($attachments_to_check[$attachment['attachname']]);
574							}
575
576							// Now anything left is bad!
577							if(count($attachments_to_check) > 0)
578							{
579								if($bad_attachments)
580								{
581									$bad_attachments = @array_merge($bad_attachments, $attachments_to_check);
582								}
583								else
584								{
585									$bad_attachments = $attachments_to_check;
586								}
587							}
588							$attachments_to_check = array();
589						}
590					}
591				}
592				closedir($dh);
593				// Any reamining to check?
594				if(!empty($attachments_to_check))
595				{
596					$attachments_to_check = array_map(array($db, "escape_string"), $attachments_to_check);
597					$attachment_names = "'".implode("','", $attachments_to_check)."'";
598					$query = $db->simple_select("attachments", "aid, attachname", "attachname IN ($attachment_names)");
599					while($attachment = $db->fetch_array($query))
600					{
601						unset($attachments_to_check[$attachment['attachname']]);
602					}
603
604					// Now anything left is bad!
605					if(count($attachments_to_check) > 0)
606					{
607						if($bad_attachments)
608						{
609							$bad_attachments = @array_merge($bad_attachments, $attachments_to_check);
610						}
611						else
612						{
613							$bad_attachments = $attachments_to_check;
614						}
615					}
616				}
617			}
618		}
619
620		$page->output_header("{$lang->orphan_attachments_search} - {$lang->step1}");
621
622		$page->output_nav_tabs($sub_tabs, 'find_orphans');
623		echo "<h3>{$lang->step1of2}</h3>";
624		echo "<p class=\"align_center\">{$lang->step1of2_line1}</p>";
625		echo "<p class=\"align_center\">{$lang->step_line2}</p>";
626		echo "<p class=\"align_center\"><img src=\"styles/{$page->style}/images/spinner_big.gif\" alt=\"{$lang->scanning}\" id=\"spinner\" /></p>";
627
628		$page->output_footer(false);
629
630		flush();
631
632		scan_attachments_directory();
633		global $bad_attachments;
634
635		$form = new Form("index.php?module=forum-attachments&amp;action=orphans&amp;step=2", "post", "redirect_form", 0, "");
636		// Scan complete
637		if(is_array($bad_attachments) && count($bad_attachments) > 0)
638		{
639			$bad_attachments = my_serialize($bad_attachments);
640			echo $form->generate_hidden_field("bad_attachments", $bad_attachments);
641		}
642		$form->end();
643		echo "<script type=\"text/javascript\">$(function() {
644				window.setTimeout(
645					function() {
646						$(\"#redirect_form\").trigger('submit');
647					}, 100
648				);
649			});</script>";
650		exit;
651	}
652}
653
654if(!$mybb->input['action'])
655{
656	$plugins->run_hooks("admin_forum_attachments_start");
657
658	if($mybb->request_method == "post" || $mybb->get_input('results', MyBB::INPUT_INT) == 1)
659	{
660		$search_sql = '1=1';
661
662		$plugins->run_hooks("admin_forum_attachments_commit_start");
663
664		// Build the search SQL for users
665
666		// List of valid LIKE search fields
667		$user_like_fields = array("filename", "filetype");
668		foreach($user_like_fields as $search_field)
669		{
670			if($mybb->get_input($search_field))
671			{
672				$search_sql .= " AND a.{$search_field} LIKE '%".$db->escape_string_like($mybb->input[$search_field])."%'";
673			}
674		}
675
676		$errors = array();
677
678		// Normal users only
679		if($mybb->get_input('user_types', MyBB::INPUT_INT) == 1)
680		{
681			$user_types = 1;
682		}
683		// Guests only
684		elseif($mybb->get_input('user_types', MyBB::INPUT_INT) == -1)
685		{
686			$user_types = -1;
687			$search_sql .= " AND a.uid='0'";
688		}
689		// Users & Guests
690		else
691		{
692			$user_types = 0;
693		}
694
695		// Username matching
696		if($mybb->input['username'])
697		{
698			$user = get_user_by_username($mybb->input['username']);
699
700			if(!$user['uid'])
701			{
702				if($user_types == 1)
703				{
704					$errors[] = $lang->error_invalid_username;
705				}
706				else
707				{
708					// Don't error if we are searching for guests or users & guests
709					$search_sql .= " AND p.username LIKE '%".$db->escape_string_like($mybb->input['username'])."%'";
710				}
711
712			}
713			else
714			{
715				$search_sql .= " AND a.uid='{$user['uid']}'";
716			}
717		}
718
719		$forum_cache = cache_forums();
720
721		// Searching for attachments in a specific forum, we need to fetch all child forums too
722		if(!empty($mybb->input['forum']))
723		{
724			if(!is_array($mybb->input['forum']))
725			{
726				$mybb->input['forum'] = array($mybb->input['forum']);
727			}
728
729			$fid_in = array();
730			foreach($mybb->input['forum'] as $fid)
731			{
732				if(!$forum_cache[$fid])
733				{
734					$errors[] = $lang->error_invalid_forums;
735					break;
736				}
737				$child_forums = get_child_list($fid);
738				$child_forums[] = $fid;
739				$fid_in = array_merge($fid_in, $child_forums);
740			}
741
742			if(count($fid_in) > 0)
743			{
744				$search_sql .= " AND p.fid IN (".implode(",", $fid_in).")";
745			}
746		}
747
748		// LESS THAN or GREATER THAN
749		$direction_fields = array(
750			"dateuploaded" => $mybb->get_input('dateuploaded', MyBB::INPUT_INT),
751			"filesize"     => $mybb->get_input('filesize', MyBB::INPUT_INT),
752			"downloads"    => $mybb->get_input('downloads', MyBB::INPUT_INT)
753		);
754
755		if(!empty($mybb->input['dateuploaded']) && $mybb->request_method == "post")
756		{
757			$direction_fields['dateuploaded'] = TIME_NOW-$direction_fields['dateuploaded']*60*60*24;
758		}
759		if(!empty($mybb->input['filesize']) && $mybb->request_method == "post")
760		{
761			$direction_fields['filesize'] *= 1024;
762		}
763
764		foreach($direction_fields as $field_name => $field_content)
765		{
766			$direction_field = $field_name."_dir";
767			if(!empty($mybb->input[$field_name]) && !empty($mybb->input[$direction_field]))
768			{
769				switch($mybb->input[$direction_field])
770				{
771					case "greater_than":
772						$direction = ">";
773						break;
774					case "less_than":
775						$direction = "<";
776						break;
777					default:
778						$direction = "=";
779				}
780				$search_sql .= " AND a.{$field_name}{$direction}'".$field_content."'";
781			}
782		}
783		if(!$errors)
784		{
785			// Lets fetch out how many results we have
786			$query = $db->query("
787				SELECT COUNT(a.aid) AS num_results
788				FROM ".TABLE_PREFIX."attachments a
789				LEFT JOIN ".TABLE_PREFIX."posts p ON (p.pid=a.pid)
790				WHERE {$search_sql}
791			");
792			$num_results = $db->fetch_field($query, "num_results");
793
794			// No matching results then show an error
795			if(!$num_results)
796			{
797				$errors[] = $lang->error_no_results;
798			}
799		}
800
801		// Now we fetch the results if there were 100% no errors
802		if(!$errors)
803		{
804			$mybb->input['page'] = $mybb->get_input('page', MyBB::INPUT_INT);
805			if($mybb->input['page'])
806			{
807				$start = ($mybb->input['page'] - 1) * $perpage;
808			}
809			else
810			{
811				$start = 0;
812				$mybb->input['page'] = 1;
813			}
814
815			switch($mybb->input['sortby'])
816			{
817				case "filesize":
818					$sort_field = "a.filesize";
819					break;
820				case "downloads":
821					$sort_field = "a.downloads";
822					break;
823				case "dateuploaded":
824					$sort_field = "a.dateuploaded";
825					break;
826				case "username":
827					$sort_field = "u.username";
828					break;
829				default:
830					$sort_field = "a.filename";
831					$mybb->input['sortby'] = "filename";
832			}
833
834			if($mybb->input['order'] != "desc")
835			{
836				$mybb->input['order'] = "asc";
837			}
838
839			$plugins->run_hooks("admin_forum_attachments_commit");
840
841			$page->add_breadcrumb_item($lang->results);
842			$page->output_header($lang->index_find_attachments);
843
844			$page->output_nav_tabs($sub_tabs, 'find_attachments');
845
846			$form = new Form("index.php?module=forum-attachments&amp;action=delete", "post");
847
848			$table = new Table;
849			$table->construct_header($form->generate_check_box('allbox', '1', '', array('class' => 'checkall')), array( 'width' => 1));
850			$table->construct_header($lang->attachments, array('colspan' => 2));
851			$table->construct_header($lang->size, array('width' => '10%', 'class' => 'align_center'));
852			$table->construct_header($lang->posted_by, array('width' => '20%', 'class' => 'align_center'));
853			$table->construct_header($lang->thread, array('width' => '25%', 'class' => 'align_center'));
854			$table->construct_header($lang->downloads, array('width' => '10%', 'class' => 'align_center'));
855			$table->construct_header($lang->date_uploaded, array("class" => "align_center"));
856
857			// Fetch matching attachments
858			$query = $db->query("
859				SELECT a.*, p.tid, p.fid, t.subject, p.uid, p.username, u.username AS user_username
860				FROM ".TABLE_PREFIX."attachments a
861				LEFT JOIN ".TABLE_PREFIX."posts p ON (p.pid=a.pid)
862				LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid)
863				LEFT JOIN ".TABLE_PREFIX."users u ON (u.uid=a.uid)
864				WHERE {$search_sql}
865				ORDER BY {$sort_field} {$mybb->input['order']}
866				LIMIT {$start}, {$perpage}
867			");
868			while($attachment = $db->fetch_array($query))
869			{
870				build_attachment_row($attachment, $table, true);
871			}
872
873			// Need to draw pagination for this result set
874			$pagination = '';
875			if($num_results > $perpage)
876			{
877				$pagination_url = "index.php?module=forum-attachments&amp;results=1";
878				$pagination_vars = array('perpage', 'sortby', 'order', 'filename', 'mimetype', 'username', 'downloads', 'downloads_dir', 'dateuploaded', 'dateuploaded_dir', 'filesize', 'filesize_dir');
879				foreach($pagination_vars as $var)
880				{
881					if($mybb->input[$var])
882					{
883						$pagination_url .= "&{$var}=".urlencode($mybb->input[$var]);
884					}
885				}
886				if(is_array($mybb->input['forum']) && !empty($mybb->input['forum']))
887				{
888					foreach($mybb->input['forum'] as $fid)
889					{
890						$pagination_url .= "&forum[]=".(int)$fid;
891					}
892				}
893				$pagination = draw_admin_pagination($mybb->input['page'], $perpage, $num_results, $pagination_url);
894			}
895
896			echo $pagination;
897			$table->output($lang->results);
898			echo $pagination;
899
900			$buttons[] = $form->generate_submit_button($lang->button_delete_attachments);
901
902			$form->output_submit_wrapper($buttons);
903			$form->end();
904
905			$page->output_footer();
906		}
907	}
908
909	$page->output_header($lang->find_attachments);
910
911	$page->output_nav_tabs($sub_tabs, 'find_attachments');
912
913	// If we have any error messages, show them
914	if($errors)
915	{
916		$page->output_inline_error($errors);
917	}
918
919	$form = new Form("index.php?module=forum-attachments", "post");
920
921	$form_container = new FormContainer($lang->find_where);
922	$form_container->output_row($lang->name_contains, $lang->name_contains_desc, $form->generate_text_box('filename', $mybb->get_input('filename'), array('id' => 'filename')), 'filename');
923	$form_container->output_row($lang->type_contains, "", $form->generate_text_box('mimetype', $mybb->get_input('mimetype'), array('id' => 'mimetype')), 'mimetype');
924	$form_container->output_row($lang->forum_is, "", $form->generate_forum_select('forum[]', $mybb->get_input('forum', MyBB::INPUT_INT), array('multiple' => true, 'size' => 5, 'id' => 'forum')), 'forum');
925	$form_container->output_row($lang->username_is, "", $form->generate_text_box('username', htmlspecialchars_uni($mybb->get_input('username')), array('id' => 'username')), 'username');
926	$form_container->output_row($lang->poster_is, "", $form->generate_select_box('user_types', array('0' => $lang->poster_is_either, '1' => $lang->poster_is_user, '-1' => $lang->poster_is_guest), $mybb->get_input('user_types', MyBB::INPUT_INT), array('id' => 'guests')), 'user_types');
927
928	$more_options = array(
929		"less_than" => $lang->more_than,
930		"greater_than" => $lang->less_than
931	);
932
933	$greater_options = array(
934		"greater_than" => $lang->greater_than,
935		"is_exactly" => $lang->is_exactly,
936		"less_than" => $lang->less_than
937	);
938
939	$form_container->output_row($lang->date_posted_is, "", $form->generate_select_box('dateuploaded_dir', $more_options, $mybb->get_input('dateuploaded_dir'), array('id' => 'dateuploaded_dir'))." ".$form->generate_numeric_field('dateuploaded', $mybb->get_input('dateuploaded', MyBB::INPUT_INT), array('id' => 'dateuploaded', 'min' => 0))." {$lang->days_ago}", 'dateuploaded');
940	$form_container->output_row($lang->file_size_is, "", $form->generate_select_box('filesize_dir', $greater_options, $mybb->get_input('filesize_dir'), array('id' => 'filesize_dir'))." ".$form->generate_numeric_field('filesize', $mybb->get_input('filesize', MyBB::INPUT_INT), array('id' => 'filesize', 'min' => 0))." {$lang->kb}", 'dateuploaded');
941	$form_container->output_row($lang->download_count_is, "", $form->generate_select_box('downloads_dir', $greater_options, $mybb->get_input('downloads_dir'), array('id' => 'downloads_dir'))." ".$form->generate_numeric_field('downloads', $mybb->get_input('downloads', MyBB::INPUT_INT), array('id' => 'downloads', 'min' => 0))."", 'dateuploaded');
942	$form_container->end();
943
944	$form_container = new FormContainer($lang->display_options);
945	$sort_options = array(
946		"filename" => $lang->filename,
947		"filesize" => $lang->filesize,
948		"downloads" => $lang->download_count,
949		"dateuploaded" => $lang->date_uploaded,
950		"username" => $lang->post_username
951	);
952	$sort_directions = array(
953		"asc" => $lang->asc,
954		"desc" => $lang->desc
955	);
956	$form_container->output_row($lang->sort_results_by, "", $form->generate_select_box('sortby', $sort_options, $mybb->get_input('sortby'), array('id' => 'sortby'))." {$lang->in} ".$form->generate_select_box('order', $sort_directions, $mybb->get_input('order'), array('id' => 'order')), 'sortby');
957	$form_container->output_row($lang->results_per_page, "", $form->generate_numeric_field('perpage', $perpage, array('id' => 'perpage', 'min' => 1)), 'perpage');
958	$form_container->end();
959
960	$buttons[] = $form->generate_submit_button($lang->button_find_attachments);
961	$form->output_submit_wrapper($buttons);
962	$form->end();
963
964	$page->output_footer();
965}
966
967/**
968 * @param array $attachment
969 * @param DefaultTable $table
970 * @param bool $use_form
971 */
972function build_attachment_row($attachment, &$table, $use_form=false)
973{
974	global $mybb, $form, $lang;
975	$attachment['filename'] = htmlspecialchars_uni($attachment['filename']);
976
977	// Here we do a bit of detection, we want to automatically check for removal any missing attachments and any not assigned to a post uploaded > 24hours ago
978	// Check if the attachment exists in the file system
979	$checked = false;
980	$title = $cell_class = '';
981	if(!file_exists(mk_path_abs($mybb->settings['uploadspath'])."/{$attachment['attachname']}"))
982	{
983		$cell_class = "bad_attachment";
984		$title = $lang->error_not_found;
985		$checked = true;
986	}
987	elseif(!$attachment['pid'] && $attachment['dateuploaded'] < TIME_NOW-60*60*24 && $attachment['dateuploaded'] != 0)
988	{
989		$cell_class = "bad_attachment";
990		$title = $lang->error_not_attached;
991		$checked = true;
992	}
993	else if(!$attachment['tid'] && $attachment['pid'])
994	{
995		$cell_class = "bad_attachment";
996		$title = $lang->error_does_not_exist;
997		$checked = true;
998	}
999	else if($attachment['visible'] == 0)
1000	{
1001		$cell_class = "invisible_attachment";
1002	}
1003
1004	if($cell_class)
1005	{
1006		$cell_class .= " align_center";
1007	}
1008	else
1009	{
1010		$cell_class = "align_center";
1011	}
1012
1013	if($use_form == true && is_object($form))
1014	{
1015		$table->construct_cell($form->generate_check_box('aids[]', $attachment['aid'], '', array('checked' => $checked)));
1016	}
1017	$table->construct_cell(get_attachment_icon(get_extension($attachment['filename'])), array('width' => 1));
1018	$table->construct_cell("<a href=\"../attachment.php?aid={$attachment['aid']}\" target=\"_blank\">{$attachment['filename']}</a>");
1019	$table->construct_cell(get_friendly_size($attachment['filesize']), array('class' => $cell_class));
1020
1021	if($attachment['user_username'])
1022	{
1023		$attachment['username'] = $attachment['user_username'];
1024	}
1025	$table->construct_cell(build_profile_link(htmlspecialchars_uni($attachment['username']), $attachment['uid'], "_blank"), array("class" => "align_center"));
1026	$table->construct_cell("<a href=\"../".get_post_link($attachment['pid'])."\" target=\"_blank\">".htmlspecialchars_uni($attachment['subject'])."</a>", array("class" => "align_center"));
1027	$table->construct_cell(my_number_format($attachment['downloads']), array("class" => "align_center"));
1028	if($attachment['dateuploaded'] > 0)
1029	{
1030		$date = my_date('relative', $attachment['dateuploaded']);
1031	}
1032	else
1033	{
1034		$date = $lang->unknown;
1035	}
1036	$table->construct_cell($date, array("class" => "align_center"));
1037	$table->construct_row();
1038}
1039