<?php
/******************************************************************************\
**   Migration script for migrating Gallery 2.x to JoomGallery  1.5.0         **
**   Version: 0.9.3                                                           **
**   By: Berend Dekens <berend@cyberwizzard.nl>                               **
**   Copyright (C) 2008 - 2009  Berend Dekens                                 **
**   Based on: JoomGallery migration from Phoca2 package                      **
**   Released under GNU GPL Public License                                    **
**   License: http://www.gnu.org/copyleft/gpl.html or have a look             **
**   at administrator/components/com_joomgallery/LICENSE.TXT                  **
\******************************************************************************/

/*******************************************************************************
**   Migration of DB and Files from Gallery2 to Joomgallery                   **
**   This script migrates albums to categories and copies all photos and      **
**   comments into the new categories.                                        **
**   ACL's, Gallery 2 users and their settings, movies etc are all ignored.   **
**   Currently, voting data is not migrated - but it should be possible to    **
**   do so in a future version                                                **
*******************************************************************************/

/*******************************************************************************
Changelog:

0.9.3:
  - Fix problems with comment import (id mismatch)

0.9.2:
  - Fix to fetch the G2 data on each page refresh, fixes problems with servers which have small session stores (for Amy)

0.9.1:
  - Fixed some typos in the debugging output (thanks to Amy)
  - Switched to assoc data storage to safe memory in the G2 extraction

0.9.0:
  - Added some GUI output
  - Cleanup of source for release
  - Added helpful messages while running
  - Fixed refreshing code
  - Added comment importing

0.8.0
  - Finished import of albums and photos
  - Added refreshing code for imports
  - Note: removed from the site because the refreshing code created an infinite loop

up to 0.7.0:
  - Internal versions to test all features

*******************************************************************************/

defined('_JEXEC') or die('Direct Access to this location is not allowed.');

/**
 * IMPORTANT! Migrating means resizing, resizing means loading the photos into memory and a lot of hosts will have allocated not nearly
 * enough memory for PHP to its thing. The following limit is enough to load and edit 12 mega pixel images so it should be enough for
 * most people. If you end up with a white screen during import you will most likely be out of memory.
 */
ini_set( "memory_limit", "100M" ); 

class Joom_Migrate_Gallery2 extends Joom_MigrationHelper{
	// These boolean flags make the migration script skip the actual action when it is processing.
	// This allows for testing - for regular use, set everything to true unless you know what you are doing!
	private $debug_migrate_categories = true;
	private $debug_migrate_photos = true;
	private $debug_migrate_comments = true;
	
	/**
	 * Constructor of class
	 *
	 * @param string $action (checks,starts or continues a migration)
	 */
	function Joom_Migrate_Gallery2($action) {
		$this->migration = 'gallery2';
		parent::__construct($action);
	}
	
	function formatBytes($bytes) {
		if ($bytes < 1024) return $bytes . ' bytes';
		$bytes = round($bytes / 1024,1);
		if ($bytes < 1024) return $bytes . ' KB';
		$bytes = round($bytes / 1024,1);
		if ($bytes < 1024) return $bytes . ' MB';
		$bytes = round($bytes / 1024,1);
		if ($bytes < 1024) return $bytes . ' GB';
		$bytes = round($bytes / 1024,1);
		if ($bytes < 1024) return $bytes . ' TB';
		echo 'Overflow';
	}

	/**
	 * checks requirements for migration
	 *
	 */
  	function Joom_Migrate_Check(){
  		echo "<div style='border: 1px dashed green; background: #99ff99; padding: 10px;'><center>";
  		echo "Menalto Gallery 2.x to JoomGallery 1.5 migration script<br/>version 0.9.3<br/>Written by <a href='http://www.cyberwizzard.nl'>Berend Dekens</a>";
  		echo "</center></div><br/>";
  		
  		// Start by checking the path
  		$g2_data_dir = $_REQUEST['g2_data_dir'];
  		if(!file_exists($g2_data_dir)) {
  			echo 'Error: Gallery 2 data path not found!';
  			return;	
  		}
  		
  		// Now make sure the 'albums' directory is found
  		$g2_data_dir .= DS . 'albums' . DS;
  		if(!file_exists($g2_data_dir)) {
  			echo 'Error: Gallery 2 data path has no "albums" folder!';
  			return;	
  		}
  		
  		// Fetch the needed data
  		$g2_db = mysql_escape_string($_REQUEST['g2_db']);							// Fetch the db name to migrate
  		$g2_prefix = mysql_escape_string($_REQUEST['g2_prefix']);					// Fetch the db prefix used for this gallery 2 installation
  		$g2_host = mysql_escape_string($_REQUEST['g2_host']);
  		$g2_user = mysql_escape_string($_REQUEST['g2_user']);
  		$g2_pass = mysql_escape_string($_REQUEST['g2_pass']);
  		$g2_debug = isset($_REQUEST['g2_debug']);

		// Store the variables we will need later on in the session
		$_SESSION['g2_data_dir'] = $g2_data_dir;
		$_SESSION['g2_prefix'] = $g2_prefix;
  		
  		// Wipe any old settings in the session cache
  		G2_Database::wipeSettings();
  		
  		// Create the utility object that manages the G2 database connection
  		$db = new G2_Database();
  		try {
  			// Save the G2 settings
  			$db->setSettings($g2_host, $g2_user, $g2_pass, $g2_db);
  			// Try to connect to the G2 database
  			$db->connect();
  		} catch (Exception $e) {
  			echo 'An error occured while trying to log in and access the Gallery 2 database, check your settings and try again.<br/><br/>Details:<br/>';
  			echo $e->message;
  			echo '<br/><pre>' . $e->getTraceAsString() . '</pre>';
  			return;	
  		}
  		
  		echo "Probe results:<ul>";
  		
  		// Count the number of albums in this Gallery 2 installation
  		$query = "SELECT COUNT(`g_id`) AS 'albums' FROM `{$g2_prefix}Entity` WHERE `g_EntityType` = 'GalleryAlbumItem';";
  		$res = $db->query($query);
  		$res = mysql_fetch_array($res);
  		$albums = $res['albums'];
  		echo "<li>Found " . $albums . " albums and/or sub-albums</li>";
  		
  		// Count the number of pictures in this Gallery 2 installation
  		$query = "SELECT COUNT(`g_id`) AS 'photos' FROM `{$g2_prefix}Entity` WHERE `g_EntityType` = 'GalleryPhotoItem';";
  		$res = mysql_fetch_array($db->query($query));
  		$photos = $res['photos'];
  		echo "<li>Found " . $photos . " photos</li>";
  		
  		// Sanity: make sure we have stuff
  		if ($photos + $albums == 0) {
  			echo "Sanity error: no albums or photos found - migration aborted!";
  			return false;	
  		}
  		
  		// Count the number of comments in this Gallery 2 installation
  		$query = "SELECT COUNT(`g_id`) AS 'comments' FROM `{$g2_prefix}Comment`;";
  		$res = mysql_fetch_array($db->query($query));
  		$comments = $res['comments'];
  		echo "<li>Found " . $comments . " comments</li>";
  		
  		// Sum all the photo sizes - this is way off if you have movies but who cares...
  		$query = "SELECT SUM(`g_size`) AS 'bytes' FROM `{$g2_prefix}DataItem`;";
  		$res = mysql_fetch_array($db->query($query));
  		echo "<li>Found roughly " . $this->formatBytes( $res['bytes'] ) . " worth of photos</li>";
  		
  		// Fetch the raw G2 data to test
		$data = self::Joom_Fetch_G2_Data($db);
		
		// Sanity - match up the image + album count with the data grab
		if($albums + $photos != count($data)) {
			echo "Sanity error: the number of albums and photos does not match the result set which has " . count($data) . " records";
			return false;
		}
  		
  		echo "</ul>";
  		
  		?>
  		<div style="border: 1px dashed red; background: #ff9999; padding: 10px;">
  			This import module is in BETA and as such is probably unreliable - USE ON YOUR OWN RISK<br/><br/>
  			<b>Make a backup</b> of your Joomla site and your Gallery 2 database <b>before</b> attempting to migrate - just in case things go wrong.
  		</div><br/><br/>
  		Supported features:
  		<ul>
  			<li>Albums and nested albums</li>
  			<li>Image replication to JoomGallery</li>
  			<li>Comments</li>
  			<li>View counts</li>
  		</ul>
  		Unsupported features:
  		<ul>
  			<li>EXIF and other information - JoomGallery will extract these on its own</li>
  			<li>Users and groups (G2 has its own users while JG uses the Joomla users)</li>
  			<li>Access Control information</li>
  			<li>Other custom tags or added information to the photos</li>
  			<li>Unpublished comments (everything is moved over)</li>
  			<li>Movies and anything other than photos</li>
  		</ul><br/>
  		
  		<b>Warning:</b> Because resuming a partially completed migration is not possible (most of the time), 
  		photos from G2 are *copied* rather than moved to their new location. Make sure you have enough space on your server!<br/><br/>
  		
  		<b>Warning:</b> Migrating large G2 installations (1000+ photos) can take very long. Run times of 30 minutes or more are not uncommon.
  		As long as the browser is in the 'loading' state, you will most likely see no visual feedback of the current progress.
  		When the migration is complete or an error occured, the page will reload showing the result.<br/>
  		If you want to see the actual progress of the migration, please look at the migration.log.txt log file, which is updated throughout
  		the migration.<br/><br/>
  		
  		Only click 'Migrate' if you have read these instructions and know that you will have to 
  		secure your private albums once the migration is complete.<br/><br/>
  		
  		<form action="index.php?option=<?php echo _JOOM_OPTION; ?>&amp;act=migrate" method="post">
                <input type="hidden" name="migration" value="gallery2" />
                <input type="hidden" name="migration_action" value="start" />
                <input type="hidden" name="g2_db" value="<?php echo $g2_db; ?>" />
                <input type="hidden" name="g2_prefix" value="<?php echo $g2_prefix; ?>" />
                <input type="hidden" name="g2_host" value="<?php echo $g2_host; ?>" />
                <input type="hidden" name="g2_user" value="<?php echo $g2_user; ?>" />
                <input type="hidden" name="g2_pass" value="<?php echo $g2_pass; ?>" />
                <input type="submit" value="Migrate" style="width:100px" />
              </form><?php
  		
  		echo "</div>";
  		
  		// Format the raw data only when debugging is enabled - this will generate a tree holding all the photos
  		// Note: we do this at the end to make sure the debug tree is shown under the normal GUI
		if($g2_debug) $struct = $this->Joom_Migrate_Parse_Gallery2_Data($data, $g2_debug);
  		
  		// If we got here, all the queries worked and everything is fine
  		return true;
	}

	/**
	 * Thanks to Amy for testing: some servers seem to have problems with the large arrays being stored in the session data. To
	 * solve this, we have to rebuild the G2 data dump each refresh.
	 *
	 * The result is a 2 dimensional array holding 7 entries per line: g_id, g_parentId, title, type, viewCount, pathComponent and timestamp
	 */
	function Joom_Fetch_G2_Data($db = null) {
		// Create the utility object that manages the G2 database connection if not provided
		if(is_null($db)) {
			$db = new G2_Database();
			try {
				// Try to connect to the G2 database using the settings which are stored in the session
				$db->connect();
			} catch (Exception $e) {
				echo 'An error occured while trying to log in and access the Gallery 2 database, check your settings and try again.<br/><br/>Details:<br/>';
				echo $e->message;
				echo '<br/><pre>' . $e->getTraceAsString() . '</pre>';
				return false;
			}
		}

		// Get the prefix from the session
		if(!isset($_SESSION['g2_prefix'])) {
			echo 'Error: the G2 table prefix was not found in the session cache. This should not happen';
			return false;
		} else
			$g2_prefix = $_SESSION['g2_prefix'];

		// Now do some serious digging: grab all photos and albums and filter out the needed information
  		$query = "SELECT
				ce.g_id AS 'g_id',
				ce.g_parentId AS 'g_parentId',
				i.g_title AS 'title',
				e.g_entityType AS 'type',
				iam.g_viewCount AS 'viewCount',
				fse.g_pathComponent AS 'pathComponent',
				i.g_originationTimestamp AS 'timestamp'
			FROM
				`{$g2_prefix}ChildEntity` AS ce,
				`{$g2_prefix}Item` AS i,
				`{$g2_prefix}Entity` AS e,
				`{$g2_prefix}ItemAttributesMap` AS iam,
				`{$g2_prefix}FileSystemEntity` AS fse
			WHERE
				ce.g_id = i.g_id AND 
				e.g_id = ce.g_id AND 
				iam.g_itemId = ce.g_id AND
				fse.g_id = ce.g_id AND
				(e.g_entityType = 'GalleryPhotoItem' OR e.g_entityType = 'GalleryAlbumItem');";

		// Run the query
		$res = $db->query($query);

		// Get all rows in one go
		$data = array();
		while($line = mysql_fetch_assoc($res)) $data[] = $line;

		return $data;
	}

	/**
	 * Utility function to fetch the G2 data from the database and parse it into a structured array.
	 * Keys: 'album_tree', 'photos'
	 */
	function Joom_Get_G2_Struct() {
		// Fetch the raw G2 data holding everything
		$data = self::Joom_Fetch_G2_Data();
		// Make sure the data is there
		if($data === false) {
			$this->Joom_Migrate_SetError('The G2 data fetch failed! Aborted');
			return false;
		}

		// Process the raw data block into a structured album tree and a photo listing
		$struct = $this->Joom_Migrate_Parse_Gallery2_Data($data, $g2_debug);
		// Make sure the parse went ok
		if(count($struct['photos']) == 0) {
			$this->Joom_Migrate_SetError('No photos found in the G2 data dump! Aborted');
			echo 'No photos found in the G2 data dump!<br/>';
			return false;
		}

		return $struct;
	}

	/**
	 * First step of the migration - we create all albums/categories in here. Because this is only database inserts, it should be able
	 * to run within the first 60 second window. After the insert we refresh the page to begin the photo migration.
	 *
	 */
	function Joom_Migrate_FirstStep() {
		// Fetch the struct with all the G2 data
		if( ($struct = self::Joom_Get_G2_Struct()) === false) return false;
		
		// Reset the resume pointer
		$_SESSION['g2_start'] = 0;
		
		// Reset the comment counter - if it is set
		$_SESSION['g2_comment_count'] = 0;

		// Grab the tree data
		$data = $struct['album_tree'];
		
		$this->Joom_Migrate_WriteLogfile ('Starting Gallery2 migration by inserting all categories');
		
		// Recursively insert all categories
		if($this->debug_migrate_categories && !$this->Recurs_Create_Cat(0, $data)) {
			$this->Joom_Migrate_SetError('Error creating categories - migration aborted');
			echo 'Error creating categories - migration aborted';
			return;
		}
		
		$this->Joom_Migrate_WriteLogfile ('Migrated all albums from Gallery 2 to JoomGallery categories');
		
		echo "Category migration complete";

		// Refresh and continue with the photo migration
		$this->Joom_Migrate_Refresh();
	}
	
	/**
	 * Utiliy function for Joom_Migrate_FirstStep to recursively create all albums
	 * @param $parentId The category ID of the current parent
	 * @param $subdata The child tree as stored with the parent
	 */
	function Recurs_Create_Cat($parentId, $subdata) {
		foreach($subdata as $id => $details) {
			$cat = (object) array(
				'id' => $id,
				'name' => mysql_escape_string($details['title']),
				'parent' => $parentId,
				'access' => 0,
				'published' => 1
			);

			// Create the category in the database
			if (!$this->Joom_Migrate_CreateCategory($cat)) {
				$this->Joom_Migrate_SetError('Error creating category ' . $cat->name . ' - canceling migration');
				echo 'Error creating category ' . $cat->name . ' - canceling migration<br/>';
				return false;
			}
			
			// Log the results to the user
			$this->Joom_Migrate_WriteLogfile ('Inserted sub-category "' . $details['title'] . '" under parent ID ' . $parentId);
			echo 'Inserted sub-category "' . $details['title'] . '" under parent ID ' . $parentId . "<br/>\n";
			
			// Recurse down
			$this->Recurs_Create_Cat($id, $details['children']);
		}
		
		return true;
	}

	/**
	 * main migration function
	 *
	 * In here we start migrating photos and we add the comments to the photo after each migration
	 *
	 */
	function Joom_Migrate_Continue() {
		echo 'NOTE: In case of problems, be sure to view the migration logs if no useful information is displayed here.<br/><br/>';
		echo 'The migration is running - please wait: if you have a lot of photos, a progress update should be shown within ' . $this->maxtime . ' seconds<br/><br/>';
		
		if(!isset($_SESSION['g2_status'])) {
			$_SESSION['g2_status'] = 'photos';
		}
		$g2_status = $_SESSION['g2_status'];
		
		switch($g2_status) {
			case 'photos':
				// Resume by restarting the photo migration
				$this->Joom_Migrate_G2_Photos();
				break;
			case 'comments':
				// Resume by restarting the comment migration
				$this->Joom_Migrate_G2_Comments();
				break;
			default:
				echo "Unknown migration status {$g2_status}";
				return false;
		}
			
	}
	
	/**
	 * This function is called from Joom_Migrate_Continue and imports all photos into the JoomGallery.
	 * The g2_status variable is used to switch from photo to comment import.
	 */
	function Joom_Migrate_G2_Photos() {
		// Fetch the struct with all the G2 data
		if( ($struct = self::Joom_Get_G2_Struct()) === false) return false;

		// Get the offset to use when starting the import
		if(!isset($_SESSION['g2_start'])) {
			$g2_start = 0;
			$_SESSION['g2_start'] = 0;	
		} else {
			$g2_start = $_SESSION['g2_start'];
		}
		
		// Load the list with all photos
		$photos = $struct['photos'];
		
		// If we starting an import, we will save the current time to the session - this way each refresh we can determine
		// how long we will need to run to import all photos
		if(!isset($_SESSION['g2_start_time']) || $g2_start == 0) {
			$_SESSION['g2_start_time'] = time();
		}
		$g2_start_time = $_SESSION['g2_start_time'];
		
		// Calculate the remaining time
		$runtime = time() - $g2_start_time;
		if($runtime <= 0) $runtime = 1;
		$max = count($photos) > 0 ? count($photos) : 1;
		$remain = ($g2_start > 0 ? round(($runtime * $max) / $g2_start) - $runtime : -1);
		echo 'Start: ' . $g2_start_time . ' runtime: ' . $runtime . '<br>';
		
		// Get the path info where we can find the photos of Gallery 2
		if(!isset($_SESSION['g2_data_dir'])) {
			$this->Joom_Migrate_WriteLogfile('Error: G2 data path not set!');
			echo 'Error: G2 data path not set!';
			return;
		} else
			$g2_data_dir = $_SESSION['g2_data_dir'];
		
		// Interval counter, every $_step imports we check the running time
		// Note: the step counter is to prevent checking the time each run, but choose it too high and you will hit max_run_time and the script
		// will terminate without redirecting.
		$count = 0;
		$_step = 5;
		
		// Send the status info to the user
		echo 'Progress: Resuming migration at photo ' . $g2_start . ' of ' . count($photos) . ' - ' . ($remain > 0 ? $remain : '-') . ' seconds remaining<br/>';
		$this->Joom_Migrate_WriteLogfile('Progress: Resuming migration at photo ' . $g2_start . ' of ' . count($photos) . ' - ' . ($remain > 0 ? $remain : '-') . ' seconds remaining - ' . $runtime . ' seconds elapsed');
		
		// Test if we have a valid array
		if(count($photos) && is_array($photos)) {
			// Test the starting point
			if($g2_start < 0) {
				$this->Joom_Migrate_WriteLogfile("Error: resume point is {$g2_start}, it should be at least 0");
				echo "Error: resume point is {$g2_start}, it should be at least 0";
				return;	
			}
			if($g2_start > count($photos) - 1) {
				$this->Joom_Migrate_WriteLogfile("Error: resume point is {$g2_start} while the list of photos is " . count($photos) . " long");
				echo "Error: resume point is {$g2_start} while the list of photos is " . count($photos) . " long";
			}

			// Continue looping
			for($i = $g2_start; $i < count($photos); $i++) {
				// Grab the photo data
				$photo = $photos[$i];

				// Create the data object used by JG to import the image
				$img = (object) array(
					'id' =>				$photo['id'],
					'catid' => 			$photo['catid'],
					'imgtitle' => 		mysql_escape_string($photo['title']),
					'imgtext' =>		'',
					'imgdate' =>		$photo['timestamp'],						// timestamp
					'imgcounter' =>		$photo['viewCount'],
					'published' =>		1,
					'imgfilename' =>	basename($photo['path']),
					//'checked_out' =>	0,
					//'approved' =>		1,
					//'ordering' =>		0
				);
				
				// Log the action in detail so problems can be detected
				$this->Joom_Migrate_WriteLogfile('Migrating ' . ($g2_data_dir . $photo['path']) . ' (' . ($g2_start+1) . '/' . count($photos) . ')');
				
				// Make sure the file exists before we try to migrate it
				if(!file_exists($g2_data_dir . $photo['path'])) {
					$this->Joom_Migrate_SetError('Can not migrate ' . ($g2_data_dir . $photo['path']) . ' because the file can not be found!');
					echo 'Can not migrate ' . ($g2_data_dir . $photo['path']) . ' because the file can not be found!';
					continue;
				}
				
				// Copy the photo
				$debug_buffer = '';
				// detail image and thumbnail will be created
				if ($this->debug_migrate_photos && !$this->Joom_Migrate_MoveAndResizeImage(
					$img, 
					$g2_data_dir . $photo['path'],
					null,
					null,
					false,
					true,	// make sure to copy the photo instead of move it - this helps if we screw up or G2 was using symlinks
					$debug_buffer
				)) {
					$this->Joom_Migrate_SetError('Warning: Can not migrate ' . ($g2_data_dir . $photo['path']) . ' due to photo resizing or moving error. ' . $debug_buffer);
					echo 'Can not migrate ' . ($g2_data_dir . $photo['path']) . ' due to photo resizing or moving error. ' . $debug_buffer . '<br/>';
					//return;	
				}
								
				// Visual feedback - one dot per photo
				echo ".";
				
				// Loop administration --------------------------------------------
				
				// Store the new starting offset
				$g2_start++;
				$_SESSION['g2_start'] = $g2_start;
				
				// Increment the counter
				$count++;
				
				// Test if we are done
				if($g2_start >= count($photos) - 1) {
					// Migration complete
					echo 'Migration complete, ' . count($photos) . ' copied into JoomGallery from Gallery 2';
					$this->Joom_Migrate_WriteLogfile('Photo migration complete, ' . count($photos) . ' photos copied into JoomGallery from Gallery 2');
					$this->Joom_Migrate_WriteLogfile('Run time: ' . $runtime . ' seconds');
					$this->Joom_Migrate_WriteLogfile('------------------------------------');
					$this->Joom_Migrate_WriteLogfile('Reloading to start comment migration');
					
					// Mark that we are done by advancing the status
					$_SESSION['g2_status'] = 'comments';
					$_SESSION['g2_start'] = 0;	// Reset the counter
					
					// Reload so we can continue the import
					$this->Joom_Migrate_Refresh();
					
					return;
				}
				
				// If we reach the boundary, test the running time - we refresh every minute and simply resume where we left off
				if($count > $_step) {
					// Reset counter
					$count = 0;
					
					// Test if we are out of time
					if(!$this->Joom_Migrate_Checktime()) {
						$this->Joom_Migrate_WriteLogfile ("We are out of time - refreshing to resume at position: " . $g2_start);
						echo '<br>We are out of time - refreshing the page to resume at position ' . $g2_start . '...';
						
						$this->Joom_Migrate_Refresh();
						return;
					}	
				}
			}
		} else {
			echo 'Error: no photos in session store! - aborted';
			return;	
		}
		
		$this->Joom_Migrate_SetError('Unknown termination error!');
		$this->Joom_Migrate_End();
	}
	
	/**
	 * This function is called from Joom_Migrate_Continue and imports all comments for all photos into the JoomGallery.
	 * The g2_status variable is used to get here.
	 */
	function Joom_Migrate_G2_Comments() {
		// Fetch the struct with all the G2 data
		if( ($struct = self::Joom_Get_G2_Struct()) === false) return false;

		// Get the offset to use when starting the import - this counter is reset when the photo migration completes
		if(!isset($_SESSION['g2_start'])) {
			$g2_start = 0;
			$_SESSION['g2_start'] = 0;	
		} else {
			$g2_start = $_SESSION['g2_start'];
		}
		
		// Load the list with all photos
		$photos = $struct['photos'];
		
		// Reconnect to the G2 database to fetch the comments for each photo
		$db = new G2_Database();
		
		// Make sure we are connected
		if(!$db->isConnected()) {
			echo 'G2 database connection was not started automatically. Perhaps you waited too long? Migration aborted.';
			$this->Joom_Migrate_SetError('G2 database connection was not started automatically. Perhaps you waited to long? Migration aborted.');
			return;	
		}
		
		// Interval counter, every $_step imports we check the running time
		// Note: the step counter is to prevent checking the time each run, but choose it too high and you will hit max_run_time and the script
		// will terminate without redirecting.
		$count = 0;
		$_step = 5;
		
		// Send the status info to the user
		echo 'Progress: Resuming comment migration at photo ' . $g2_start . ' of ' . count($photos) . '<br/>';
		$this->Joom_Migrate_WriteLogfile('Progress: Resuming comment migration at photo ' . $g2_start . ' of ' . count($photos));
		
		// Test if we have a valid array
		if(count($photos) && is_array($photos)) {
			// Test the starting point
			if($g2_start < 0) {
				$this->Joom_Migrate_WriteLogfile("Error: comment resume point is {$g2_start}, it should be at least 0");
				echo "Error: comment resume point is {$g2_start}, it should be at least 0";
				return;	
			}
			if($g2_start > count($photos) - 1) {
				$this->Joom_Migrate_WriteLogfile("Error: comment resume point is {$g2_start} while the list of photos is " . count($photos) . " long");
				echo "Error: comment resume point is {$g2_start} while the list of photos is " . count($photos) . " long";
			}

			// Continue looping
			for($i = $g2_start; $i < count($photos); $i++) {
				// Grab the photo data
				$photo = $photos[$i];
				
				// Log that we are migrating these comments
				$this->Joom_Migrate_WriteLogfile('Migrating comments for photo ' . $photo['g_id'] . ' ' . $i);
				
				// Generate the query to load the comments for the given photo
				$query = "SELECT
							ce.g_parentId AS 'cmtpic',
							c.g_host AS 'cmtip',
							c.g_subject AS 'cmtname',
							c.g_comment AS 'cmttext',
							c.g_date AS 'cmtdate',
							1 AS 'published'
						FROM
							g2_Comment AS c,
							g2_ChildEntity AS ce
						WHERE
							c.g_id = ce.g_id AND
							ce.g_parentId = '" . $photo['id'] . "';";
				
				// Actually get the comments
				$res = $db->query($query);
				$comments = array();
				
				// Fetch all comments into an array of objects
				while($row = mysql_fetch_object($res)) {
					$row->cmtname = mysql_escape_string($row->cmtname);
					$row->cmttext = mysql_escape_string($row->cmttext);
					$comments[] = $row;	
				}

				// Make sure we actually have comments to migrate
				if(count($comments)) {
					$this->Joom_Migrate_WriteLogfile('Migrating ' . count($comments) . ' comments for photo ' . $photo['id'] . ' to JoomGallery');

					$comment_count = $this->Joom_Migrate_Comments($comments);
					$_SESSION['g2_comment_count'] += $comment_count;

					if($comment_count != count($comments)) {
						$this->Joom_Migrate_SetError("Error storing comments for photo " . $photo['id']);
						echo "Error storing comments for photo " . $photo['id'] . '<br/>';
					}
				}
								
				// Visual feedback - one dot per photo
				echo ".";
				
				// Loop administration --------------------------------------------
				
				// Store the new starting offset
				$g2_start++;
				$_SESSION['g2_start'] = $g2_start;
				
				// Increment the counter
				$count++;
				
				// Test if we are done
				if($g2_start >= count($photos) - 1) {
					// Migration complete
					echo 'Comment migration complete, all comments copied into JoomGallery from Gallery 2';
					$this->Joom_Migrate_WriteLogfile('Comments migration complete, all ' . $_SESSION['g2_comment_count'] . ' comments copied into JoomGallery from Gallery 2');
					
					// Destroy session stuff
					unset($_SESSION['g2_start']);
					
					// Finish the migration
					$this->Joom_Migrate_WriteLogfile ("end of migration - exiting");
					$this->Joom_Migrate_WriteLogfile ("*****************************");
					$this->Joom_Migrate_CloseLogfile();
					$this->Joom_Migrate_End();
					
					return;
				}
				
				// If we reach the boundary, test the running time - we refresh every minute and simply resume where we left off
				if($count > $_step) {
					// Reset counter
					$count = 0;
					
					// Test if we are out of time
					if(!$this->Joom_Migrate_Checktime()) {
						$this->Joom_Migrate_WriteLogfile ("We are out of time while importing comments - refreshing to resume at position: " . $g2_start);
						echo '<br>We are out of time while importing comments - refreshing the page to resume at position ' . $g2_start . '...';
						
						$this->Joom_Migrate_Refresh();
						return;
					}	
				}
			}
		} else {
			echo 'Error: no photos in session store! - comment import aborted';
			return;	
		}
		
		$this->Joom_Migrate_SetError('Unknown termination error while importing comments!');
		$this->Joom_Migrate_End();
	}
	
	/**
	 * This function parses the array resulting from the Gallery 2 query and converts it into a structured array.
	 *
	 * Expected array keys for each row:
	 *	g_id: 				The internal Gallery 2 id
	 *	g_parentId:			The parent id of this album or photo
	 *	title:				The title or name of the album or photo
	 *	type:				The Gallery2 type, GalleryAlbumItem or GalleryPhotoItem
	 *	viewCount:			Number of views for this photo or album
	 *	pathComponent:		Relative path component of this album or photo.
	 *						The full path for a photo can be found by prefixing the filename by the parent albums paths.
	 *	timestamp:			Timestamp of creation of the item
	 *
	 * @param $data The 2 dimensional array holding all data loaded from Gallery 2
	 * @param $debug Set to true to get a HTML dump of the created tree structure while the tree map and photo list is assembled
	 * @return array Array has 2 keys: 'album_tree' holding the tree with album and 'photos' holding a list of all photos in Gallery 2
	 */
	function Joom_Migrate_Parse_Gallery2_Data($data, $debug = false) {
		$tree = array();
		$photos = array();
		
		// Find the root node - the one with parent 0
		for($i = 0; $i < count($data); $i++) {
			$line = $data[$i];
			if($line['g_parentId'] == 0 && $line['type'] == 'GalleryAlbumItem') {
				// This is the root node, don't bother storing data about it but recurse
				$this->Joom_Migrate_Recurse_Data_Tree($data, $tree, $photos, '', $line['g_id'], $debug);
			}
		}
		
		return array(
			'album_tree' => $tree,
			'photos' => $photos
		);
	}
	
	/**
	 *
	 * @param $data The array holding the 6 columns of data as retrieved from the G2 database
	 * @param $album_tree A tree (array) holding the album structure
	 * @param $photos A flat array holding all photos, the filenames will be prefix by the path as constructed by the album tree
	 * @param $path The current path that followed by following the album tree (recursively passed down)
	 * @param $parentId The current parent node to examine (process its children)
	 * @param $debug Defaults to true - this will format and output the tree in HTML while it is getting parsed
	 */
	function Joom_Migrate_Recurse_Data_Tree($data, &$album_tree, &$photos, $path = '', $parentId, $debug = false) {
		// Find each item which has the selected parentId and process it
		// If it is an album, we recurse down into it, if its a photo, we add it to the photo listing and set its path
		foreach($data as $line) {
			if($line['g_parentId'] == $parentId) {
				// This is a child for the given parent
				if($line['type'] == 'GalleryAlbumItem') {
					// This is an album, add it
					$album_tree[$line['g_id']] = array(
						'title' => $line['title'],
						'viewCount' => $line['viewCount'],
						'path' => $line['pathComponent'],
						'timestamp' => $line['timestamp'],
						'children' => array()
					);
					
					if($debug) {
						echo '<div style="border: 1px dashed grey; background: #99ff99">Title: ' . $line['title'] . '<br/>';
						echo 'Type: Album<br/>';
						echo 'View count: ' . $line['viewCount'] . '<br/>';
						echo 'Path: ' . $line['pathComponent'] . "<br/>\n";
						echo "<div style='padding-left: 20px;'>";
					}
					
					// Now recurse down
					$this->Joom_Migrate_Recurse_Data_Tree(
						$data, 
						$album_tree[$line['g_id']]['children'], 
						$photos, 
						$path . DS . $line['pathComponent'], 
						$line['g_id'], 
						$debug
					);
					
					if($debug) {
						echo "</div></div>";	
					}
				} else {
					$photos[] = array(
						'title' => $line['title'],
						'id' => $line['g_id'],
						'catid' => $parentId,
						'viewCount' => $line['viewCount'],
						'timestamp' => $line['timestamp'],
						'path' => $path . DS . $line['pathComponent']
					);
					
					if($debug) {
						echo '<div style="background: white;">';
						echo 'Title: ' . $line['title'] . '<br/>';
						echo 'Type: Photo<br/>';
						echo 'View count: ' . $line['viewCount'] . '<br/>';
						echo 'Path: ' . $path . DS . $line['pathComponent'] . "<br/>--------------------------------<br/>\n";
						echo '</div>';
					}
				}	
			}
		}
	}
}

if(isset($show_jmtablerow)) {
?>
          <tr>
            <td>
              <h4><?php echo JText::sprintf('JGA_CHECKMIGRATION', 'Gallery2'); ?></h4>
            </td>
            <td align="center">
              <form action="index.php?option=<?php echo _JOOM_OPTION; ?>&amp;act=migrate" method="post">
                <input type="hidden" name="migration" value="gallery2" />
                <input type="hidden" name="migration_action" value="check" />
                <table>
                	<tr><td>Gallery2 database:</td><td><input type="text" name="g2_db" value="gallery2" /></td></tr>
                	<tr><td>Table prefix:</td><td><input type="text" name="g2_prefix" value="g2_" /></td></tr>
                	<tr><td>Gallery2 data path:</td><td><input type="text" name="g2_data_dir" value="/var/www/yoursite/htdocs/gallery/g2data" /></td></tr>
                	<tr><td colspan="2"><hr></td></tr>
                	<tr><td>Database host:</td><td><input type="text" name="g2_host" value="localhost" /></td></tr>
                	<tr><td>Database user:</td><td><input type="text" name="g2_user" value="root" /></td></tr>
                	<tr><td>Database password:</td><td><input type="text" name="g2_pass" value="mysecretpassword" /></td></tr>
                	<tr><td colspan="2"><hr></td></tr>
                	<tr><td>Debug:</td><td><input type="checkbox" name="g2_debug"/></td></tr>
                </table>
                <input type="submit" value="<?php echo JText::_('JGA_MIGRATION_CHECKMIGRATION'); ?>" style="width:100px" />
              </form>
            </td>
          </tr>
<?php
}

class G2_Database {
	// These variables hold the connection settings to access the G2 database
	private $g2_host = 'localhost';
	private $g2_username = '';
	private $g2_password = '';
	private $g2_database = 'gallery2';
	
	private $has_settings = false;
	
	// The current handle to the database
	private $g2_handle = null;
	
	function __construct() {
		if(isset($_SESSION['g2_host'])) {
			// Settings are stored in the session - load them into this instance
			$this->g2_host = $_SESSION['g2_host'];	
			$this->g2_username = $_SESSION['g2_username'];
			$this->g2_password = $_SESSION['g2_password'];
			$this->g2_database = $_SESSION['g2_database'];
			
			// Mark the fact that we have settings
			$this->has_settings = true;
			
			// Now we have settings - open up the connection
			$this->connect();
		}
	}
	
	/**
	 * Start the connection to the database to access the G2 data. Normally this is a different database using even different credentials so
	 * this wrapps the connection.
	 */
	function connect() {
		if($this->has_settings && $this->g2_handle == null) {
			$this->g2_handle = mysql_connect($this->g2_host, $this->g2_username, $this->g2_password);
			if($this->g2_handle === false) {
				$this->g2_handle = null;
			 	throw new Exception("G2_Database::connect failed: " . mysql_error());
			}
			
			if (!mysql_select_db($this->g2_database)) {
				throw new Exception("G2_Database::connect select database failed: " . mysql_error());
			}
		}
	}
	
	function setSettings($host, $username, $password, $database) {
		$this->g2_host = $host;						// Store the G2 database host
		$this->g2_username = $username;				// Save the G2 login name
		$this->g2_password = $password;				// Save the password as well
		$this->g2_database = $database;				// The database used for G2
		
		// Store the variables into the session
		$_SESSION['g2_host'] = $host;
		$_SESSION['g2_username'] = $username;
		$_SESSION['g2_password'] = $password;
		$_SESSION['g2_database'] = $database;
		
		// Mark the settings
		$this->has_settings = true;	
	}
	
	/**
	 * Run the query on the G2 database
	 */
	function query($query) {
		if($this->g2_handle == null) die('G2_Database::query failed as there is no connection');
		
		// Run the query
		$res = mysql_query($query, $this->g2_handle) or die('G2_Database::query failed: ' . mysql_error());
		
		// Return the result
		return $res;
	}
	
	static function wipeSettings() {
		unset($_SESSION['g2_host']);
		unset($_SESSION['g2_username']);
		unset($_SESSION['g2_password']);
		unset($_SESSION['g2_database']);
	}
	
	function isConnected() {
		return $this->g2_handle != null;
	}
	
}

?>