1<?php
2# MantisBT - a php based bugtracking system
3
4# MantisBT is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 2 of the License, or
7# (at your option) any later version.
8#
9# MantisBT is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * This upgrade moves attachments from the database to the disk
19 * @package MantisBT
20 * @copyright Copyright 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
21 * @copyright Copyright 2002  MantisBT Team - mantisbt-dev@lists.sourceforge.net
22 * @link http://www.mantisbt.org
23 */
24
25require_once( dirname( dirname( __FILE__ ) ) . '/core.php' );
26
27form_security_validate( 'move_attachments_project_select' );
28
29access_ensure_global_level( config_get_global( 'admin_site_threshold' ) );
30
31$f_file_type         = gpc_get( 'type' );
32$f_project_to_move  = gpc_get( 'to_move', null );
33
34/**
35 * Moves attachments from the specified list of projects from disk to database
36 * @param string $p_type Attachment type ('bug' or 'project')
37 * @param array $p_projects List of projects to process
38 * @return array summary of moves per project
39 */
40function move_attachments_to_db( $p_type, $p_projects ) {
41	if( empty( $p_projects ) ) {
42		return array();
43	}
44
45	# Build the SQL query based on attachment type
46	$t_file_table = '{' . $p_type . '_file}';
47	switch( $p_type ) {
48		case 'project':
49			$t_query = "SELECT f.*
50				FROM {project_file} f
51				WHERE content = ''
52				  AND f.project_id = " . db_param() . "
53				ORDER BY f.filename";
54			break;
55		case 'bug':
56			$t_query = "SELECT f.*
57				FROM {bug_file} f
58				JOIN {bug} b ON b.id = f.bug_id
59				WHERE content = ''
60				  AND b.project_id = " . db_param() . "
61				ORDER BY f.bug_id, f.filename";
62			break;
63	}
64
65	# Process projects list
66	foreach( $p_projects as $t_project ) {
67		# Retrieve attachments for the project
68		$t_result = db_query( $t_query, array( $t_project ) );
69
70		# Project upload path
71		$t_upload_path = project_get_field( $t_project, 'file_path' );
72		if( is_blank( $t_upload_path ) ) {
73			$t_upload_path = config_get_global( 'absolute_path_default_upload_folder' );
74		}
75
76		if( is_blank( $t_upload_path )
77			|| !file_exists( $t_upload_path )
78			|| !is_dir( $t_upload_path )
79		) {
80			# Invalid path
81			$t_failures = db_num_rows( $t_result );
82			$t_data = "ERROR: Upload path '$t_upload_path' does not exist or is not accessible";
83		} else {
84			# Process attachments
85			$t_failures = 0;
86			$t_data = array();
87
88			while( $t_row = db_fetch_array( $t_result ) ) {
89				# read file from disk
90				$t_filename = $t_row['folder'] . $t_row['diskfile'];
91
92				if ( !file_exists( $t_filename ) ) {
93					$t_status = "Original File Not Found '$t_filename'";
94					$t_failures++;
95				} else {
96					$c_content = db_prepare_binary_string( fread( fopen( $t_filename, 'rb' ), $t_row['filesize'] ) );
97
98					# write file to db
99					if( db_is_oracle() ) {
100						db_update_blob( $t_file_table, 'content', $c_content, "id=" . (int)$t_row['id'] );
101						$t_query = "UPDATE $t_file_table SET folder='' WHERE id = " . db_param();
102						$t_result2 = db_query( $t_query, array( (int)$t_row['id'] ) );
103					} else {
104						$t_update_query = "UPDATE $t_file_table
105										SET folder = " . db_param() . ",
106										content = " . db_param() . "
107										WHERE id = " . db_param();
108						$t_result2 = db_query( $t_update_query,
109							array( '', $c_content, (int)$t_row['id'] )
110						);
111					}
112
113					if( !$t_result2 ) {
114						$t_status = 'Database update failed';
115						$t_failures++;
116					} else {
117						$t_status = "'$t_filename' moved to database";
118					}
119				}
120
121				# Add the file and status to the list of processed attachments
122				$t_file = array(
123					'id' => $t_row['id'],
124					'filename' => $t_row['filename'],
125					'status' => $t_status,
126				);
127				if( $p_type == 'bug' ) {
128					$t_file['bug_id'] = $t_row['bug_id'];
129				}
130				$t_data[] = $t_file;
131			}
132		}
133
134		$t_moved[] = array(
135			'name'       => project_get_name( $t_project ),
136			'path'       => $t_upload_path,
137			'rows'       => db_num_rows( $t_result ),
138			'failed'     => $t_failures,
139			'data'       => $t_data,
140		);
141
142	}
143	return $t_moved;
144}
145
146/**
147 * Moves attachments from the specified list of projects from database to disk
148 * @param string $p_type     Attachment type ('bug' or 'project').
149 * @param array  $p_projects List of projects to process.
150 * @return array summary of moves per project
151 */
152function move_attachments_to_disk( $p_type, array $p_projects ) {
153	if( empty( $p_projects ) ) {
154		return array();
155	}
156
157	# Build the SQL query based on attachment type
158	switch( $p_type ) {
159		case 'project':
160			$t_query = 'SELECT f.*
161				FROM {project_file} f
162				WHERE content <> \'\'
163				  AND f.project_id = ' . db_param() . '
164				ORDER BY f.filename';
165			break;
166		case 'bug':
167			$t_query = 'SELECT f.*
168				FROM {bug_file} f
169				JOIN {bug} b ON b.id = f.bug_id
170				WHERE content <> \'\'
171				  AND b.project_id = ' . db_param() . '
172				ORDER BY f.bug_id, f.filename';
173			break;
174	}
175
176	# Process projects list
177	foreach( $p_projects as $t_project ) {
178		# Retrieve attachments for the project
179		$t_result = db_query( $t_query, array( $t_project ) );
180
181		# Project upload path
182		$t_upload_path = project_get_upload_path( $t_project );
183		if( is_blank( $t_upload_path )
184			|| !file_exists( $t_upload_path )
185			|| !is_dir( $t_upload_path )
186			|| !is_writable( $t_upload_path )
187		) {
188			# Invalid path
189			$t_failures = db_num_rows( $t_result );
190			$t_data = 'ERROR: Upload path \'' . $t_upload_path . '\' does not exist or is not writeable';
191		} else {
192			# Process attachments
193			$t_failures = 0;
194			$t_data = array();
195
196			while( $t_row = db_fetch_array( $t_result ) ) {
197				$t_disk_filename = $t_upload_path . $t_row['diskfile'];
198				if ( file_exists( $t_disk_filename ) ) {
199					$t_status = 'Disk File Already Exists \'' . $t_disk_filename . '\'';
200					$t_failures++;
201				} else {
202					# write file to disk
203					if( file_put_contents( $t_disk_filename, $t_row['content'] ) ) {
204						# successful, update database
205						# @todo do we want to check the size of data transfer matches here?
206						switch( $p_type ) {
207							case 'project':
208								$t_update_query = 'UPDATE {project_file}
209									SET folder = ' . db_param() . ', content = \'\'
210									WHERE id = ' . db_param();
211								break;
212							case 'bug':
213								$t_update_query = 'UPDATE {bug_file}
214									SET folder = ' . db_param() . ', content = \'\'
215									WHERE id = ' . db_param();
216								break;
217						}
218						$t_update_result = db_query(
219							$t_update_query,
220							array( $t_upload_path, $t_row['id'] )
221						);
222
223						if( !$t_update_result ) {
224							$t_status = 'Database update failed';
225							$t_failures++;
226						} else {
227							$t_status = 'Moved to \'' . $t_disk_filename . '\'';
228						}
229					} else {
230						$t_status = 'Copy to \'' . $t_disk_filename . '\' failed';
231						$t_failures++;
232					}
233				}
234
235				# Add the file and status to the list of processed attachments
236				$t_file = array(
237					'id' => $t_row['id'],
238					'filename' => $t_row['filename'],
239					'status' => $t_status,
240				);
241				if( $p_type == 'bug' ) {
242					$t_file['bug_id'] = $t_row['bug_id'];
243				}
244				$t_data[] = $t_file;
245			}
246		}
247
248		$t_moved[] = array(
249			'name'       => project_get_name( $t_project ),
250			'path'       => $t_upload_path,
251			'rows'       => db_num_rows( $t_result ),
252			'failed'     => $t_failures,
253			'data'       => $t_data,
254		);
255
256	}
257	return $t_moved;
258}
259
260form_security_purge( 'move_attachments_project_select' );
261
262# Page header, menu
263layout_page_header( 'MantisBT Administration - Moving Attachments' );
264
265layout_admin_page_begin();
266
267?>
268
269<div class="col-md-12 col-xs-12">
270	<div class="space-10"></div>
271
272<?php
273
274if( null == $f_project_to_move ) {
275	echo '<div class="alert alert-danger">';
276	echo '<p class="lead"><strong>Opps!</strong> Please select the project you want to move the attachment.</p>';
277	echo '</div>';
278} else {
279
280	$t_moved = array();
281
282	foreach( $f_project_to_move as $t_project_to_move ) {
283
284		$t_array = explode( ':', $t_project_to_move );
285
286		if( isset( $t_array[1] ) ) {
287			$t_project_id = $t_array[1];
288
289			switch( $t_array[0] ) {
290				case 'disk':
291					$t_moved[] = move_attachments_to_disk( $f_file_type, array( $t_project_id ) );
292					break;
293				case 'db':
294					$t_moved[] = move_attachments_to_db( $f_file_type, array( $t_project_id ) );
295					break;
296			}
297		}
298	}
299
300	# Display results
301	if( empty( $t_moved ) ) {
302		echo '<div class="alert alert-danger">';
303		echo '<p class="lead">Nothing to do.</p>';
304		echo '</div>';
305	} else {
306		foreach( $t_moved as $t_row ) {
307			$t_row = $t_row[0];
308
309			echo '<div class="widget-box widget-color-blue2">';
310			echo '<div class="widget-header widget-header-small">';
311			echo '<h4 class="widget-title lighter">';
312			print_icon( 'fa-paperclip', 'ace-icon' );
313			printf(
314				"Project '%s' : %d attachments %s",
315				$t_row['name'],
316				$t_row['rows'],
317				( 0 == $t_row['failed']
318					? 'moved successfully'
319					: 'to move, ' . $t_row['failed'] . ' failures')
320			);
321			echo '</h4>';
322			echo '</div>';
323			echo '<div class="widget-body">';
324			echo '<div class="widget-main no-padding">';
325			if( is_array( $t_row['data'] ) ) {
326				# Display details of moved attachments
327				echo '<div class="table-responsive">';
328				echo '<table class="table table-bordered table-condensed table-hover table-striped">';
329				echo '<thead>';
330				echo '<tr>',
331					$f_file_type == 'bug' ? '<td width="5%">Bug ID</td>' : '',
332					'<td width="3%">File ID</td><th width="15%">Filename</td><td width="25%">Status</td>',
333					'</tr>';
334				echo '</thead>';
335				echo '<tbody>';
336				foreach( $t_row['data'] as $t_data ) {
337					echo '<tr>';
338					if( $f_file_type == 'bug' ) {
339						printf( '<td>%s</td>', bug_format_id( $t_data['bug_id'] ) );
340					}
341					printf( '<td>%s</td><td>%s</td><td>%s</td></tr>',
342						$t_data['id'],
343						$t_data['filename'],
344						$t_data['status'] );
345				}
346				echo '</tbody>';
347				echo '</table>';
348				echo '</div>';
349
350			} else {
351				# No data rows - display error message
352				echo '<div class="alert alert-danger">';
353				echo '<p>' . $t_row['data'] . '</p>';
354				echo '</div>';
355			}
356			echo '</div>';
357			echo '</div>';
358			echo '</div>';
359			echo '<br/>';
360		}
361	}
362}
363echo '<br/>';
364print_link_button( 'system_utils.php', 'Back to System Utilities' );
365
366echo '</div>';
367
368layout_admin_page_end();
369