diff --git a/admin/move_attachments.php b/admin/move_attachments.php
new file mode 100644
index 0000000000..a79a6bc151
--- /dev/null
+++ b/admin/move_attachments.php
@@ -0,0 +1,218 @@
+.
+
+/**
+ * This upgrade moves attachments from the database to the disk
+ * @package MantisBT
+ * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
+ * @copyright Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net
+ * @link http://www.mantisbt.org
+ */
+
+
+/**
+ * MantisBT Core API's
+ */
+require_once( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'core.php' );
+
+form_security_validate( 'move_attachments_project_select' );
+
+access_ensure_global_level( config_get_global( 'admin_site_threshold' ) );
+
+
+$f_file_type = gpc_get( 'type' );
+$f_projects_to_disk = gpc_get( 'to_disk', null );
+
+
+/**
+ * Moves attachments from the specified list of projects from database to disk
+ * @param string $p_type Attachment type ('bug' or 'project')
+ * @param array $p_projects List of projects to process
+ * @return array summary of moves per project
+ */
+function move_attachments_to_disk( $p_type, $p_projects ) {
+ if( empty( $p_projects ) ) {
+ return array();
+ }
+
+ # Build the SQL query based on attachment type
+ $t_file_table = db_get_table( "mantis_${p_type}_file_table" );
+ switch( $p_type ) {
+ case 'project':
+
+ $t_query = "SELECT f.*
+ FROM $t_file_table f
+ WHERE content <> ''
+ AND f.project_id = " . db_param() . "
+ ORDER BY f.filename";
+ break;
+
+ case 'bug':
+ $t_bug_table = db_get_table( 'mantis_bug_table' );
+
+ $t_query = "SELECT f.*
+ FROM $t_file_table f
+ JOIN $t_bug_table b ON b.id = f.bug_id
+ WHERE content <> ''
+ AND b.project_id = " . db_param() . "
+ ORDER BY f.bug_id, f.filename";
+ break;
+ }
+
+ # Process projects list
+ foreach( $p_projects as $t_project ) {
+ # Retrieve attachments for the project
+ $t_result = db_query_bound( $t_query, array( $t_project ) );
+
+ # Project upload path
+ $t_upload_path = project_get_upload_path( $t_project );
+ if( is_blank( $t_upload_path )
+ || !file_exists( $t_upload_path )
+ || !is_dir( $t_upload_path )
+ || !is_writable( $t_upload_path )
+ ) {
+ # Invalid path
+ $t_failures = db_num_rows( $t_result );
+ $t_data = "ERROR: Upload path '$t_upload_path' does not exist or is not writable";
+ } else {
+ # Process attachments
+ $t_failures = 0;
+ $t_data = array();
+
+ if( $p_type == 'project' ) {
+ $t_seed = config_get( 'document_files_prefix', null, ALL_USERS, $t_project ) . $t_project;
+ }
+
+ while( $t_row = db_fetch_array( $t_result ) ) {
+ if( $p_type == 'bug' ) {
+ $t_seed = $t_row['bug_id'] . $t_row['filename'];
+ }
+
+ $t_filename = $t_upload_path . file_generate_unique_name( $t_seed, $t_upload_path );
+
+ # write file to disk
+ if( file_put_contents( $t_filename, $t_row['content'] ) ) {
+ # successful, update database
+ # @todo do we want to check the size of data transfer matches here?
+ $t_update_query = "UPDATE $t_file_table
+ SET diskfile = " . db_param() . ",
+ folder = " . db_param() . ",
+ content = ''
+ WHERE id = " . db_param();
+ $t_update_result = db_query_bound(
+ $t_update_query,
+ array( $t_filename, $t_upload_path, $t_row['id'] )
+ );
+
+ if( !$t_update_result ) {
+ $t_status = 'Database update failed';
+ $t_failures++;
+ } else {
+ $t_status = "Moved to '$t_filename'";
+ }
+ } else {
+ $t_status = "Copy to '$t_file_name' failed";
+ $t_failures++;
+ }
+
+ # Add the file and status to the list of processed attachments
+ $t_file = array(
+ 'id' => $t_row['id'],
+ 'filename' => $t_row['filename'],
+ 'status' => $t_status,
+ );
+ if( $p_type == 'bug' ) {
+ $t_file['bug_id'] = $t_row['bug_id'];
+ }
+ $t_data[] = $t_file;
+ }
+ }
+
+ $t_moved[] = array(
+ 'name' => project_get_name( $t_project ),
+ 'path' => $t_upload_path,
+ 'rows' => db_num_rows( $t_result ),
+ 'failed' => $t_failures,
+ 'data' => $t_data,
+ );
+
+ }
+ return $t_moved;
+}
+
+
+$t_moved = move_attachments_to_disk( $f_file_type, $f_projects_to_disk );
+
+form_security_purge( 'move_attachments_project_select' );
+
+$t_redirect_url = 'system_utils.php';
+
+# Page header, menu
+html_page_top(
+ 'MantisBT Administration - Moving Attachments',
+ empty( $t_result ) ? $t_redirect_url : null
+);
+
+?>
+
+
+
+Nothing to do.\n";
+} else {
+ foreach( $t_moved as $t_row ) {
+ printf(
+ "
Project '%s' : %d attachments %s.
\n",
+ $t_row['name'],
+ $t_row['rows'],
+ ( 0 == $t_row['failed']
+ ? 'moved successfully'
+ : 'to move, ' . $t_row['failed'] . ' failures')
+ );
+
+ if( is_array( $t_row['data'] ) ) {
+ # Display details of moved attachments
+ echo '
', "\n",
+ '',
+ $f_file_type == 'bug' ? 'Bug ID | ' : '',
+ 'File | Filename | Status | ',
+ '
';
+ foreach( $t_row['data'] as $t_data ) {
+ echo '';
+ if( $f_file_type == 'bug' ) {
+ printf( '%s | ', bug_format_id( $t_data['bug_id'] ) );
+ }
+ printf( '%s | %s | %s |
' . "\n",
+ $t_data['id'],
+ $t_data['filename'],
+ $t_data['status']
+ );
+ }
+ echo '
';
+ } else {
+ # No data rows - display error message
+ echo '
' . $t_row['data'] . '
';
+ }
+ echo '
';
+ }
+}
+
+print_bracket_link( $t_redirect_url, 'Back to System Utilities' );
+
+html_page_bottom();
diff --git a/admin/move_attachments_page.php b/admin/move_attachments_page.php
new file mode 100644
index 0000000000..c199c87896
--- /dev/null
+++ b/admin/move_attachments_page.php
@@ -0,0 +1,160 @@
+.
+
+/**
+ * This upgrade moves attachments from the database to the disk
+ *
+ * @package MantisBT
+ * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
+ * @copyright Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net
+ * @link http://www.mantisbt.org
+ */
+
+
+/**
+ * MantisBT Core API's
+ */
+require_once( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'core.php' );
+
+access_ensure_global_level( config_get_global( 'admin_site_threshold' ) );
+
+# Page header, menu
+html_page_top( 'MantisBT Administration - Moving Attachments' );
+echo '
';
+print_bracket_link( helper_mantis_url( 'admin/system_utils.php' ), 'Back to System Utilities' );
+echo '
';
+
+
+# File type should be 'bug' (default) or 'project'
+$f_file_type = gpc_get( 'type', 'bug' );
+
+$t_bug_table = db_get_table( 'mantis_bug_table' );
+$t_project_table = db_get_table( 'mantis_project_table' );
+
+switch( $f_file_type ) {
+ case 'project':
+ $t_type = 'Project Files';
+ $t_file_table = db_get_table( 'mantis_project_file_table' );
+ $t_query = "SELECT p.id, p.name, COUNT(f.id) disk
+ FROM $t_file_table f
+ LEFT JOIN $t_project_table p ON p.id = f.project_id
+ WHERE content <> ''
+ GROUP BY p.id, p.name
+ ORDER BY p.name";
+ break;
+
+ case 'bug':
+ default:
+ $t_type = 'Attachments';
+ $t_file_table = db_get_table( 'mantis_bug_file_table' );
+ $t_query = "SELECT p.id, p.name, COUNT(f.id) disk
+ FROM $t_file_table f
+ JOIN $t_bug_table b ON b.id = f.bug_id
+ JOIN $t_project_table p ON p.id = b.project_id
+ WHERE content <> ''
+ GROUP BY p.id, p.name
+ ORDER BY p.name";
+ break;
+}
+
+# Move to disk: projects having non-empty attachments in the DB
+$t_result = db_query_bound( $t_query );
+
+# Build list, excluding projects having upload method other than DISK
+$t_projects = array();
+while( $t_row = db_fetch_array( $t_result ) ) {
+ $t_project_id = (int) $t_row['id'];
+ $t_upload_method = config_get( 'file_upload_method', null, ALL_USERS, $t_project_id );
+ if( $t_upload_method == DISK ) {
+ $t_projects[$t_project_id] = $t_row;
+ }
+}
+
+if( count( $t_projects ) == 0 ) {
+ # Nothing to do
+ echo '
No attachments need to be moved.
';
+} else {
+ # Display name for All Projects
+ if( isset( $t_projects[ALL_PROJECTS] ) ) {
+ $t_projects[ALL_PROJECTS]['name'] = 'All Projects';
+ }
+
+ # Display table of projects for user selection
+
+?>
+
+
+
+.
-
-/**
- * This upgrade moves attachments from the database to the disk
- * @package MantisBT
- * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
- * @copyright Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net
- * @link http://www.mantisbt.org
- */
-/**
- * MantisBT Core API's
- */
-require_once( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'core.php' );
-
-access_ensure_global_level( config_get_global( 'admin_site_threshold' ) );
-
-// Move type should be attachment or project.
-$f_move_type = gpc_get( 'doc' );
-
-function get_prefix( $file_path ) {
- if( substr( $file_path, 0, 1 ) == '/' ) {
-
- # Unix absolute
- return '';
- }
- if( substr( $file_path, 0, 1 ) == '\\' ) {
-
- # Windows absolute
- return '';
- }
- if( substr( $file_path, 1, 2 ) == ':\\' ) {
-
- # Windows absolute
- return '';
- }
- return dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR;
-}
-
-# ------ move file attachments to issues from database to disk
-# select non-empty data fields
-# match with the project to get the file path
-# store the file in the correct folder
-#
-# Assumptions: only supports storage in local file system (not FTP)
-# file paths are set up and working
-#
-# Re-running this is safe because the data
-# is not removed from the database until it is successfully copied.
-#
-function upgrade_move_att2disk( $p_source ) {
-
- # $p_source is the string "attachment" or "project"
- if( $p_source == 'attachment' ) {
- $t_file_table = db_get_table( 'bug_file' );
- $t_bug_label = "Bug";
- }
- if( $p_source == 'project' ) {
- $t_file_table = db_get_table( 'project_file' );
- $t_bug_label = "Project";
- }
-
- # check that the source was valid
- if( !isset( $t_file_table ) ) {
- echo 'Failure: Internal Error: File source not set';
- return;
- }
-
- # check that the destination is set up properly
- $t_upload_method = config_get_global( 'file_upload_method' );
- if( $t_upload_method <> DISK ) {
- echo 'Failure: Upload Method is not DISK';
- return;
- }
-
- $query = 'SELECT * FROM ' . $t_file_table . ' WHERE content <> \'\'';
-
- $result = @db_query_bound( $query );
-
- if( false == $result ) {
- echo '
No attachments need to be moved.
';
- return;
- }
-
- $count = db_num_rows( $result );
- echo '
Found ' . $count . ' attachments to be moved.
';
- $t_failures = 0;
-
- if( $count > 0 ) {
- echo '
';
-
- # Headings
- echo '' . $t_bug_label . ' | Attachment | Status |
';
- }
-
- for( $i = 0;$i < $count;$i++ ) {
- $t_row = db_fetch_array( $result );
-
- // trace bug id back to project to determine the proper file path
- if( $p_source == 'attachment' ) {
- $t_project_id = bug_get_field( $t_row['bug_id'], 'project_id' );
- $t_bug_id = $t_row['bug_id'];
- } else {
- $t_project_id = (int) $t_row['project_id'];
- $t_bug_id = $t_project_id;
- }
-
- $t_file_path = project_get_field( $t_project_id, 'file_path' );
- $prefix = get_prefix( $t_file_path );
- $t_real_file_path = $prefix . $t_file_path;
- $c_filename = file_clean_name( $t_row['filename'] );
-
- printf( "\n%8d | %s | ", helper_alternate_class(), $t_bug_id, $t_row['filename'] );
-
- if( is_blank( $t_real_file_path ) || !file_exists( $t_real_file_path ) || !is_dir( $t_real_file_path ) || !is_writable( $t_real_file_path ) ) {
- echo 'Destination ' . $t_real_file_path . ' not writable';
- $t_failures++;
- } else {
- $t_file_name = $t_real_file_path . $c_filename;
-
- // write file to disk store after adjusting the path
- if( file_put_contents( $t_file_name, $t_row['content'] ) ) {
- // successful, update database
- /** @todo do we want to check the size of data transfer matches here? */
- $c_new_file_name = $t_file_path . $c_filename;
- $query2 = "UPDATE $t_file_table SET diskfile = " . db_param() . ",
- folder = " . db_param() . ", content = '' WHERE id = " . db_param();
- $update = @db_query_bound( $query2, Array( $c_new_file_name, $t_file_path, $t_row['id'] ) );
- if( !$update ) {
- echo 'database update failed';
- $t_failures++;
- } else {
- echo 'moved to ' . $t_file_name;
- }
- } else {
- echo 'copy to ' . $t_file_name . ' failed';
- $t_failures++;
- }
- }
-
- echo ' |
';
- }
-
- echo '
' . $count . ' attachments processed, ' . $t_failures . ' failures';
-}
-
-# ---------------------
-# main code
-#
-if( $f_move_type == 'attachment' ) {
- $t_type = 'Attachments';
-} else {
- if( $f_move_type == 'project' ) {
- $t_type = 'Project Files';
- } else {
- echo "
Invalid value '$f_move_type' for parameter 'doc'.
";
- exit;
- }
-}
-?>
-
-
-
-
MantisBT Administration - Move to Disk
-
-
-
-
-
-
-
-Completed...';
-?>
-
-
diff --git a/admin/system_utils.php b/admin/system_utils.php
index 611f36f74b..867362f412 100644
--- a/admin/system_utils.php
+++ b/admin/system_utils.php
@@ -52,12 +52,12 @@
Move attachments stored in database schema to disk files. |
- 'attachment' ) );?>
+ 'bug' ) );?>
|
Move project files stored in database schema to disk. |
- 'project' ) );?>
+ 'project' ) );?>
|
diff --git a/core/file_api.php b/core/file_api.php
index cc1b8660be..697f725953 100644
--- a/core/file_api.php
+++ b/core/file_api.php
@@ -981,6 +981,8 @@ function file_get_content( $p_file_id, $p_type = 'bug' ) {
* @param int $p_bug_id ID of bug containing attachments to be moved
* @param int $p_project_id_to destination project ID for the bug
* @return null
+ *
+ * @todo: this function can't cope with source or target storing attachments in DB
*/
function file_move_bug_attachments( $p_bug_id, $p_project_id_to ) {
$t_project_id_from = bug_get_field( $p_bug_id, 'project_id' );
diff --git a/core/project_api.php b/core/project_api.php
index 67b566923d..da2236625f 100644
--- a/core/project_api.php
+++ b/core/project_api.php
@@ -652,6 +652,30 @@ function project_get_all_user_rows( $p_project_id = ALL_PROJECTS, $p_access_leve
return array_values( $t_users );
}
+/**
+ * Returns the upload path for the specified project, empty string if
+ * file_upload_method is DATABASE
+ * @param int $p_project_id
+ * @return string upload path
+ */
+function project_get_upload_path( $p_project_id ) {
+
+ if( DATABASE == config_get( 'file_upload_method', null, ALL_USERS, $p_project_id ) ) {
+ return '';
+ }
+
+ if( $p_project_id == ALL_PROJECTS ) {
+ $t_path = config_get( 'absolute_path_default_upload_folder', '', ALL_USERS, ALL_PROJECTS );
+ } else {
+ $t_path = project_get_field( $p_project_id, 'file_path' );
+ if( is_blank( $t_path ) ) {
+ $t_path = config_get( 'absolute_path_default_upload_folder', '', ALL_USERS, $p_project_id );
+ }
+ }
+
+ return $t_path;
+}
+
# ===================================
# Data Modification
# ===================================