Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+164 -42
View File
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Converter Library Class
*/
abstract class _Library
{
/**
@@ -79,6 +82,25 @@ abstract class _Library
$this->software = $software;
}
/**
* Libraries
*
* @return array
*/
public static function libraries()
{
return array(
'core' => 'IPS\convert\Library\Core',
'blog' => 'IPS\convert\Library\Blog',
'calendar' => 'IPS\convert\Library\Calendar',
'cms' => 'IPS\convert\Library\Cms',
'downloads' => 'IPS\convert\Library\Downloads',
'forums' => 'IPS\convert\Library\Forums',
'gallery' => 'IPS\convert\Library\Gallery',
'nexus' => 'IPS\convert\Library\Nexus',
);
}
/**
* When called at the start of a conversion step, indicates that we are using a specific key for WHERE clauses which nets performance improvements
*
@@ -106,21 +128,23 @@ abstract class _Library
/**
* Processes a conversion cycle.
*
* @param integer $data Data from the previous step.
* @param \IPS\convert\App $app Application Class for the current conversion
* @param integer $data Data from the previous step.
* @param string $method Conversion method we are processing
* @param int|NULL $perCycle The number of results to process per cycle
* @return array|NULL Data for the MultipleRedirect
*/
public function process( $data, $method, $perCycle=NULL )
{
if ( !is_null( $perCycle ) )
if ( !\is_null( $perCycle ) )
{
static::$perCycle = $perCycle;
}
/* temp */
$classname = get_class( $this->software );
$classname = \get_class( $this->software );
$canConvert = $classname::canConvert();
/* If we hit something that can't be converted, there's a problem */
if( $canConvert === NULL )
{
return NULL;
@@ -128,24 +152,35 @@ abstract class _Library
static::$action = $canConvert[ $method ]['table'];
static::$startValue = $data;
$masterAppId = $this->software->app->getMasterConversionId();
if ( !isset( $_SESSION['convertCountRows'] ) )
{
$_SESSION['convertCountRows'] = array();
}
if ( !isset( $_SESSION['convertCountRows'][ $method ] ) )
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ] ) )
{
$_SESSION['convertCountRows'][ $method ] = $this->software->countRows( static::$action, $canConvert[ $method ]['where'], true );
$_SESSION['convertCountRows'][ $masterAppId ] = array();
}
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ] = array();
}
$total = $_SESSION['convertCountRows'][ $method ];
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ] = $this->software->countRows( static::$action, $canConvert[ $method ]['where'], true );
}
$total = $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ];
if ( $data >= $total )
{
$completed = $this->software->app->_session['completed'];
$more_info = $this->software->app->_session['more_info'];
if ( !in_array( $method, $completed ) )
if ( !\in_array( $method, $completed ) )
{
$completed[] = $method;
}
@@ -156,9 +191,10 @@ abstract class _Library
$this->software->app->_session = array( 'working' => array(), 'more_info' => $more_info, 'completed' => $completed, 'running' => $running );
unset( $_SESSION['currentKeyValue'] );
unset( $_SESSION['convertCountRows'][ $method ] );
unset( $_SESSION['convertContinue'] );
return NULL;
$percentage = ( 100 / static::getTotalCachedRows( $this->software->app->getMasterConversionId() ) ) * $this->_getConvertedCount();
return array( 0, sprintf( \IPS\Member::loggedIn()->language()->get( 'converted_x_of_x' ), $total, $total, \IPS\Member::loggedIn()->language()->addToStack( '_' . $this->getMethodFromMenuRows( $method )['step_title'] ) ), $percentage );
}
else
{
@@ -178,6 +214,7 @@ abstract class _Library
break;
case 'convertPrivateMessages':
case 'convertPrivateMessageReplies':
$table = 'convert_link_pms';
break;
}
@@ -185,13 +222,20 @@ abstract class _Library
/* Select our max foreign ID */
$type = $this->getMethodFromMenuRows( $method )['link_type'];
/* @todo If we ever support converting more than one CMS database, we'll need to change this */
if( is_array( $type ) )
if( \is_array( $type ) )
{
$type = $type[0];
}
$lastId = \IPS\Db::i()->select( 'MAX(CAST( foreign_id AS UNSIGNED) )', $table, array( "app=? AND type=?", $this->software->app->app_id, $type ) )->first();
try
{
$lastId = \IPS\Db::i()->select( 'foreign_id', $table, array( "app=? AND type=?", $this->software->app->app_id, $type ), 'link_id DESC', 1 )->first();
}
catch( \UnderflowException $e )
{
// Nothing has been ran yet
$lastId = 0;
}
/* Set the data count for MR */
$data = \IPS\Db::i()->select( 'COUNT(*)', $table, array( "app=? AND type=?", $this->software->app->app_id, $type ) )->first();
@@ -207,6 +251,7 @@ abstract class _Library
}
/* Unset this so we don't do this again. */
$_SESSION['convertContinue'] = false;
unset( $_SESSION['convertContinue'] );
}
@@ -223,12 +268,12 @@ abstract class _Library
\IPS\Output::i()->error( 'converter_could_not_continue', '2V387/1', 403, '' );
}
/* Generate dots for UI to show that processing is still occuring */
/* Generate dots for UI to show that processing is still occurring */
$dots = str_repeat( '.', $_SESSION['convertSkipped'] );
/* Sleep to allow the other process to complete without hammering the server */
sleep(5);
return array( $data, \IPS\Member::loggedIn()->language()->get( 'waiting_previous_process' ) . $dots, 100 / $total * ( $data ) );
return array( $data, \IPS\Member::loggedIn()->language()->get( 'waiting_previous_process' ) . $dots, ( 100 / static::getTotalCachedRows( $this->software->app->getMasterConversionId() ) ) * $this->_getConvertedCount( $total ) );
}
/* Set conversion as running */
@@ -244,7 +289,7 @@ abstract class _Library
/* A Software Exception indicates we are done */
$completed = $this->software->app->_session['completed'];
$more_info = $this->software->app->_session['more_info'];
if ( !in_array( $method, $completed ) )
if ( !\in_array( $method, $completed ) )
{
$completed[] = $method;
}
@@ -256,7 +301,9 @@ abstract class _Library
$this->software->app->_session = array( 'working' => array(), 'more_info' => $more_info, 'completed' => $completed, 'running' => $running );
unset( $_SESSION['currentKeyValue'] );
unset( $_SESSION['convertContinue'] );
return NULL;
$percentage = ( 100 / static::getTotalCachedRows( $this->software->app->getMasterConversionId() ) ) * $this->_getConvertedCount();
return array( 0, sprintf( \IPS\Member::loggedIn()->language()->get( 'converted_x_of_x' ), $total, $total, \IPS\Member::loggedIn()->language()->addToStack( '_' . $this->getMethodFromMenuRows( $method )['step_title'] ) ), $percentage );
}
catch( \Exception $e )
{
@@ -284,15 +331,63 @@ abstract class _Library
$running[ $method ] = FALSE;
$this->software->app->_session = array_merge( $this->software->app->_session, array( 'working' => array( $method => $data + static::$perCycle ), 'running' => $running ) );
return array( $data + static::$perCycle, sprintf( \IPS\Member::loggedIn()->language()->get( 'converted_x_of_x' ), ( $data + static::$perCycle > $total ) ? $total : $data + static::$perCycle, \IPS\Member::loggedIn()->language()->addToStack( $this->getMethodFromMenuRows( $method )['step_title'] ), $total ), 100 / $total * ( $data + static::$perCycle ) );
$percentage = ( 100 / static::getTotalCachedRows( $this->software->app->getMasterConversionId() ) ) * $this->_getConvertedCount( ( $data + static::$perCycle > $total ) ? $total : $data + static::$perCycle );
return array( $data + static::$perCycle, sprintf( \IPS\Member::loggedIn()->language()->get( 'converted_x_of_x' ), ( $data + static::$perCycle ) < $total ? $data + static::$perCycle : $total, $total, \IPS\Member::loggedIn()->language()->addToStack( '_' . $this->getMethodFromMenuRows( $method )['step_title'] ) ), $percentage );
}
}
/**
* Return the count of everything converted so far
*
* @param int $add Amount to add to completed count
* @return int
*/
protected function _getConvertedCount( $add = 0 )
{
if( $this->software->app->parent )
{
$masterAppId = $this->software->app->_parent->app_id;
$applicationsToCheck[] = $this->software->app->_parent;
$applicationsToCheck = array_merge( $applicationsToCheck, iterator_to_array( $this->software->app->_parent->children() ) );
}
else
{
$masterAppId = $this->software->app->app_id;
$applicationsToCheck[] = $this->software->app;
$applicationsToCheck = array_merge( $applicationsToCheck, iterator_to_array( $this->software->app->children() ) );
}
$totalSoFar = 0;
foreach( $applicationsToCheck as $app )
{
/* Check app has cached data */
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $app->sw ] ) )
{
continue;
}
$totalSoFar += array_sum(
array_filter( $_SESSION['convertCountRows'][ $masterAppId ][ $app->sw ], function( $key ) use( $app ) {
return ( \in_array( $key, $app->_session['completed'] ) );
}, ARRAY_FILTER_USE_KEY )
);
}
if( $add )
{
$totalSoFar += $add;
}
return $totalSoFar;
}
/**
* Empty Conversion Data
*
* @param integer $data Data from the previous step.
* @param \IPS\convert\App $app Application Class for the current conversion
* @param integer $data Data from the previous step.
* @param \IPS\convert\App $method The conversion method to empty results for
* @return array|NULL Data for the MultipleRedirect
*/
public function emptyData( $data, $method )
@@ -300,7 +395,7 @@ abstract class _Library
$perCycle = 500;
/* temp */
$classname = get_class( $this->software );
$classname = \get_class( $this->software );
$canConvert = $this->menuRows();
if ( !isset( $canConvert[ $method ]['link_type'] ) )
@@ -314,7 +409,7 @@ abstract class _Library
{
$count = 0;
/* Just one type? */
if ( !is_array( $type ) )
if ( !\is_array( $type ) )
{
foreach( array( 'convert_link', 'convert_link_pms', 'convert_link_posts', 'convert_link_topics' ) as $table )
{
@@ -346,7 +441,7 @@ abstract class _Library
try
{
/* If we're dealing with more than one type, then we can just delete from any one at random until we're done */
if ( is_array( $type ) )
if ( \is_array( $type ) )
{
$type = array_rand( $type );
}
@@ -406,7 +501,7 @@ abstract class _Library
/**
* Truncates data from local database
*
* @param string Convert method to run truncate call for.
* @param string $method Convert method to run truncate call for
* @return void
*/
public function emptyLocalData( $method )
@@ -442,7 +537,17 @@ abstract class _Library
{
default:
$key = $linkType ?: $table;
$toDelete['convert_link'][ $key ] = $key;
if( \is_array( $key ) )
{
foreach( $key as $link )
{
$toDelete['convert_link'][ $link ] = $link;
}
}
else
{
$toDelete['convert_link'][ $key ] = $key;
}
break;
case 'core_message_topics':
case 'core_message_posts':
@@ -505,7 +610,7 @@ abstract class _Library
return $this->convertable;
}
$classname = get_class( $this->software );
$classname = \get_class( $this->software );
$this->convertable = $classname::canConvert();
if( $this->convertable === NULL )
@@ -528,22 +633,23 @@ abstract class _Library
* Magic __call() method
*
* @param string $name The method to call without convert prefix.
* @param mixed $arguements Arguments to pass to the method
* @param mixed $arguments Arguments to pass to the method
* @return mixed
*/
public function __call( $name, $arguments )
{
if ( method_exists( $this, 'convert' . $name ) )
{
return call_user_func( array( $this, 'convert' . $name ), $arguments );
$function = 'convert' . $name;
return $this->$function( $arguments );
}
elseif ( method_exists( $this, $name ) )
{
return call_user_func( array( $this, $name ), $arguments );
return $this->$name( $arguments );
}
else
{
\IPS\Log::log( "Call to undefined method in " . get_class( $this ) . "::{$name}", 'converters' );
\IPS\Log::log( "Call to undefined method in " . \get_class( $this ) . "::{$name}", 'converters' );
return NULL;
}
}
@@ -555,11 +661,25 @@ abstract class _Library
*/
public function getPostConversionInformation()
{
$appId = $this->software->app->parent ?: $this->software->app->app_id;
return \IPS\Member::loggedIn()->language()->addToStack( 'conversion_info_message', FALSE, array( 'sprintf' => array(
(string) \IPS\Http\Url::internal( "app=convert&module=manage&controller=create&_new=1" ),
(string) \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=finish&id={$appId}" )
) ) );
return '';
}
/**
* Return a count of cached convertable rows
*
* @param int $appId Parent conversion APP ID
* @return int|null
*/
public static function getTotalCachedRows( int $appId ) :?int
{
if( empty( $_SESSION['convertCountRows'][ $appId ] ) )
{
return 0;
}
return array_sum( array_map( function( $app ) {
return array_sum( $app );
}, $_SESSION['convertCountRows'][ $appId ] ) );
}
/**
@@ -574,20 +694,22 @@ abstract class _Library
* Utility method to run queries for menuRows() data
*
* @param array $return menuRow() data
* @param bool $ips Count IPS rows
* @param bool $source Count source rows
* @return array
* @throws \UnderflowException
* @throws \IPS\convert\Exception
*/
public function getDatabaseRowCounts( array $return )
public function getDatabaseRowCounts( array $return, $ips=TRUE, $source=TRUE )
{
foreach( $return as $key => $value )
{
if( isset( $value['ips_rows'] ) AND $value['ips_rows'] instanceof \IPS\Db\Select )
if( $ips AND isset( $value['ips_rows'] ) AND $value['ips_rows'] instanceof \IPS\Db\Select )
{
$return[ $key ]['ips_rows'] = (int) $value['ips_rows']->first();
}
if( isset( $return[ $key ]['source_rows']['table'] ) )
if( $source AND isset( $return[ $key ]['source_rows']['table'] ) )
{
$return[ $key ]['source_rows'] = $this->software->countRows( $value['source_rows']['table'], $value['source_rows']['where'] );
}
@@ -631,7 +753,7 @@ abstract class _Library
/**
* Returns an array of tables that need to be truncated when Empty Local Data is used
*
* @param string The method to truncate
* @param string $method The method to truncate
* @return array
*/
abstract protected function truncate( $method );