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

+204 -87
View File
@@ -5,21 +5,30 @@
* @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 Application Class
*/
class _App extends \IPS\Patterns\ActiveRecord
{
/**
* @brief [ActiveRecord] Caches
* @note Defined cache keys will be cleared automatically as needed
*/
protected $caches = array( 'convert_apps' );
/**
* @brief [ActiveRecord] ID Database Column
*/
@@ -30,11 +39,6 @@ class _App extends \IPS\Patterns\ActiveRecord
*/
public static $databaseTable = 'convert_apps';
/**
* @brief [ActiveRecord] Database ID Fields
*/
protected static $databaseIdFields = array( 'name' );
/**
* @brief Array Storage of loaded ID links.
*/
@@ -58,9 +62,19 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* Get converted apps
*
* @return array
* @return \IPS\Patterns\ActiveRecordIterator
*/
public static function apps()
{
return new \IPS\Patterns\ActiveRecordIterator( new \ArrayIterator( static::getStore() ), 'IPS\convert\App' );
}
/**
* Get all converter apps
*
* @return array
*/
public static function getStore()
{
if ( !isset( \IPS\Data\Store::i()->convert_apps ) )
{
@@ -82,9 +96,8 @@ class _App extends \IPS\Patterns\ActiveRecord
\IPS\Data\Store::i()->convert_apps = $rows;
}
return new \IPS\Patterns\ActiveRecordIterator( new \ArrayIterator( \IPS\Data\Store::i()->convert_apps ), 'IPS\convert\App' );
return \IPS\Data\Store::i()->convert_apps;
}
/**
@@ -107,13 +120,11 @@ class _App extends \IPS\Patterns\ActiveRecord
) );
}
$classname = get_class( $this->getSource( TRUE, FALSE ) );
$classname = \get_class( $this->getSource( TRUE, FALSE ) );
$this->login = ( $classname::loginEnabled() === TRUE ) ? 1 : 0;
$this->db_driver = 'mysql'; /* I was going to drop this, but it has the potential for expansion in the future, as all we do is select from the source */
$this->app_merge = 1;
parent::save();
unset( \IPS\Data\Store::i()->convert_apps );
}
/**
@@ -132,8 +143,6 @@ class _App extends \IPS\Patterns\ActiveRecord
\IPS\Db::i()->delete( 'convert_logs', array( 'log_app=?', $this->app_id ) );
parent::delete();
unset( \IPS\Data\Store::i()->convert_apps );
}
/**
@@ -144,7 +153,7 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* Save Session Data for this application
*
* @param array Session Data
* @param array $value Session Data
* @return void
*/
public function set__session( $value )
@@ -209,6 +218,7 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* [Legacy] Automatically fix any legacy application keys that have changed.
*
* @param string $value App value
* @return void
*/
public function set_sw( $value )
@@ -242,7 +252,7 @@ class _App extends \IPS\Patterns\ActiveRecord
*/
public function get__parent()
{
if ( is_null( $this->parentStore ) )
if ( \is_null( $this->parentStore ) )
{
if ( ! $this->parent )
{
@@ -265,6 +275,22 @@ class _App extends \IPS\Patterns\ActiveRecord
return $this->parentStore;
}
/**
* Return this application's children applications
*
* @return array|\IPS\Patterns\ActiveRecordIterator
*/
public function children()
{
/* If we have a parent, we are already a child and will not have children */
if( $this->parent )
{
return array();
}
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'convert_apps', array( 'parent=?', $this->app_id ) ), 'IPS\convert\App' );
}
/**
* Retrieves an Invision Community ID from a Foreign ID.
@@ -279,46 +305,35 @@ class _App extends \IPS\Patterns\ActiveRecord
*/
public function getLink( $foreign_id, $type, $parent=FALSE, $mainTable=FALSE )
{
if ( !is_array( $type ) )
if ( !\is_array( $type ) )
{
$type = array( $type );
}
$key = md5( json_encode( $type ) );
$key = \count( $type ) > 1 ? md5( implode('', $type ) ) : $type[0];
if ( isset( $this->linkCache[ $key ][ $foreign_id ] ) )
{
if( $this->linkCache[ $key ][ $foreign_id ] === FALSE )
{
throw new \OutOfRangeException( 'link_invalid' );
}
return $this->linkCache[ $key ][ $foreign_id ];
}
$table = 'convert_link';
if ( $mainTable === FALSE )
foreach ( $type as $t )
{
foreach( $type AS $t )
{
if ( in_array( $t, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) )
{
if ( in_array( $t, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
{
$table = 'convert_link_pms';
}
else
{
$tableType = str_replace( 'forums_', '', $t );
$table = "convert_link_{$tableType}";
}
}
}
$table = $this->_getLinkTableName( $t, $mainTable );
}
try
{
$link = \IPS\Db::i()->select( 'ipb_id', $table, array( \IPS\Db::i()->in( 'type', $type ) . ' AND foreign_id=? AND app=?', (string) $foreign_id, ( $parent === TRUE AND $this->parent ) ? $this->parent : $this->app_id ), 'link_id DESC' )->first();
$link = \IPS\Db::i()->select( 'ipb_id', $table, array( \IPS\Db::i()->in( 'type', $type ) . ' AND foreign_id=? AND app=?', (string) $foreign_id, ( $parent === TRUE and $this->parent ) ? $this->parent : $this->app_id ), 'link_id DESC' )->first();
$this->linkCache[ $key ][ $foreign_id ] = $link;
return $this->linkCache[ $key ][ $foreign_id ];
}
catch( \UnderflowException $e )
catch ( \UnderflowException $e )
{
/* If lookup failed, and we have a parent, try it anyway */
try
@@ -333,6 +348,23 @@ class _App extends \IPS\Patterns\ActiveRecord
throw new \OutOfRangeException( 'link_invalid' );
}
}
/**
* Get conversion ID of the master core conversion
*
* @return int
*/
public function getMasterConversionId(): int
{
try
{
return $this->_parent->app_id;
}
catch( \BadMethodCallException $e )
{
return $this->app_id;
}
}
/**
* @brief Sibling Link Cache
@@ -377,16 +409,15 @@ class _App extends \IPS\Patterns\ActiveRecord
* @param string $type The type of item
* @param boolean $duplicate If TRUE, then this item is a duplicate and was merged into existing $ips_id
* @param boolean $mainTable If TRUE, then link will be stored in the main convert_link table even if $type is 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts'
*
* @return void
*/
public function addLink( $ips_id, $foreign_id, $type, $duplicate=FALSE, $mainTable=FALSE )
{
$table = 'convert_link';
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) AND $mainTable === FALSE )
if ( \in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) AND $mainTable === FALSE )
{
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
if ( \in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
{
$table = 'convert_link_pms';
}
@@ -407,30 +438,89 @@ class _App extends \IPS\Patterns\ActiveRecord
$this->linkCache[$type][$foreign_id] = $ips_id;
}
/**
* Pre-cache link data based on source data
*
* @param array $data
* @param array $map
*/
public function preCacheLinks( array $data, array $map ): void
{
$cachesToLookup = [];
$tables = [];
/* Check for data */
if( !\count( $data ) )
{
return;
}
foreach( $map as $type => $columns )
{
$cachesToLookup[ $type ] = array();
if ( \is_array( $columns ) )
{
foreach ( $columns as $column )
{
$cachesToLookup[ $type ] = \array_merge( $cachesToLookup[ $type ], \array_column( $data, $column ) );
}
$cachesToLookup[ $type ] = \array_map( 'strval', \array_unique( $cachesToLookup[ $type ] ) );
}
else
{
$cachesToLookup[ $type ] = \array_map( 'strval', \array_unique( \array_column( $data, $columns ) ) );
}
/* Check whether there are values we don't have cached */
if( isset( $this->linkCache[ $type ] ) )
{
$cachesToLookup[ $type ] = \array_diff( $cachesToLookup[ $type ], \array_keys( $this->linkCache[ $type ] ) );
if ( !\count( $cachesToLookup[ $type ] ) )
{
/* nope, so skip the query */
continue;
}
}
$tables[ $type ] = $this->_getLinkTableName( $type );
}
foreach( $tables as $link => $table )
{
if( !isset( $this->linkCache[ $link ] ) )
{
$this->linkCache[ $link ] = array();
}
$where = [ '`type`=? AND ' . \IPS\Db::i()->in('foreign_id', $cachesToLookup[ $link ] ) . ' AND (app=? OR app=?)', $link, $this->parent, $this->app_id ];
foreach( \IPS\Db::i()->select( 'ipb_id, foreign_id', $table, $where ) as $result )
{
$this->linkCache[ $link ][ (string) $result['foreign_id'] ] = $result['ipb_id'];
}
foreach( $cachesToLookup[ $link ] as $foreignId )
{
if( !isset( $this->linkCache[ $link ][ (string) $foreignId ] ) )
{
$this->linkCache[ $link ][ (string) $foreignId ] = FALSE;
}
}
}
}
/**
* Checks to see if a link exists for an Invision Community ID
*
* @param integer $ips_id The Invision Community ID.
* @param integer $ips_id The Invision Community ID.
* @param string $type The type of item
* @param bool $mainTable If TRUE, then link will be check against the main convert_link table even if $type is 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts'
* @return void
* @throws \OutOfRangeException
*/
public function checkLink( $ips_id, $type, $mainTable=FALSE )
{
$table = 'convert_link';
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) AND $mainTable === FALSE )
{
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
{
$table = 'convert_link_pms';
}
else
{
$tableType = str_replace( 'forums_', '', $type );
$table = "convert_link_{$tableType}";
}
}
$table = $this->_getLinkTableName( $type, $mainTable );
try
{
@@ -466,31 +556,19 @@ class _App extends \IPS\Patterns\ActiveRecord
*
* @param mixed $foreignId Foreign data ID
* @param string $type The type of item
* @return void
*/
public function deleteLink( $foreignId, $type )
{
/* Build cache key */
if ( !is_array( $type ) )
if ( !\is_array( $type ) )
{
$type = array( $type );
}
$key = md5( json_encode( $type ) );
$key = \count( $type ) > 1 ? md5( implode('', $type ) ) : $type[0];
$table = 'convert_link';
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) )
{
if ( in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
{
$table = 'convert_link_pms';
}
else
{
$tableType = str_replace( 'forums_', '', $type );
$table = "convert_link_{$tableType}";
}
}
$table = $this->_getLinkTableName( $type );
\IPS\Db::i()->delete( $table, array( 'foreign_id=? AND type=? AND app=?', $foreignId, $type, $this->app_id ) );
@@ -510,7 +588,7 @@ class _App extends \IPS\Patterns\ActiveRecord
* Construct an \IPS\convert\App object for a sibling application.
*
* @param string $software The application key to look for
* @return \IPS\convert\APP
* @return \IPS\convert\App
* @throws \OutOfRangeException
*/
public function getSibling( $software )
@@ -533,8 +611,8 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* Fetch the source application class file.
*
* @param bool Construct the object
* @param bool Establish a database connection ($construct must be TRUE)
* @param bool $construct Construct the object
* @param bool $needDB Establish a database connection ($construct must be TRUE)
* @return \IPS\convert\Software|string
* @throws \InvalidArgumentException
*/
@@ -551,7 +629,7 @@ class _App extends \IPS\Patterns\ActiveRecord
}
/* Change any app keys to newer versions */
switch( $this->data['app_key'] )
switch( $this->_data['app_key'] )
{
case 'photopost8':
case 'photopost7':
@@ -593,15 +671,21 @@ class _App extends \IPS\Patterns\ActiveRecord
$this->app_key = 'vbulletin5';
$this->save();
break;
}
if ( ! class_exists( 'IPS\\convert\\Software\\' . ucwords( $this->_data['sw'] ) . '\\' . ucwords( $this->_data['app_key'] ) ) )
/* Make sure UBBThreads is now all lowercase */
case 'UBBthreads':
$this->app_key = 'ubbthreads';
$this->save();
break;
}
$classname = \IPS\convert\Software::software()[ $this->_data['sw'] ][ $this->_data['app_key'] ];
if ( ! class_exists( $classname ) )
{
throw new \InvalidArgumentException( 'invalid_source' );
}
$classname = 'IPS\\convert\\Software\\' . ucwords( $this->_data['sw'] ) . '\\' . ucwords( $this->_data['app_key'] );
if ( $construct )
{
return new $classname( $this, $needDB );
@@ -618,12 +702,13 @@ class _App extends \IPS\Patterns\ActiveRecord
* @param string $message The message to log.
* @param string $method The current conversion method (convert_posts, convert_topics, etc.)
* @param integer $severity The severity level of the log. Default to LOG_NOTICE
* @param int|NULL $id The item ID
* @return void
* @throws \InvalidArgumentException
*/
public function log( $message, $method, $severity=1, $id=NULL )
{
if ( ! in_array( $severity, array( static::LOG_NOTICE, static::LOG_WARNING, static::LOG_ERROR ) ) )
if ( ! \in_array( $severity, array( static::LOG_NOTICE, static::LOG_WARNING, static::LOG_ERROR ) ) )
{
throw new \InvalidArgumentException( 'invalid_severity' );
}
@@ -641,12 +726,12 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* Callback function to return all dependencies not yet converted.
*
* @param string Value from depency array
* @param string $value Value from depency array
* @return boolean
*/
public function dependencies( $value )
{
if ( !in_array( $value, $this->_session['completed'] ) )
if ( !\in_array( $value, $this->_session['completed'] ) )
{
return TRUE;
}
@@ -657,6 +742,7 @@ class _App extends \IPS\Patterns\ActiveRecord
/**
* Saves information about the current step to the session.
*
* @param string $method The converison method
* @param array $values Values from the form.
* @return void
*/
@@ -695,6 +781,7 @@ class _App extends \IPS\Patterns\ActiveRecord
*
* @param string $method Method name
* @param bool $status Flag status
* @return void
*/
public function setRunningFlag( $method, $status )
{
@@ -726,4 +813,34 @@ class _App extends \IPS\Patterns\ActiveRecord
return FALSE;
}
/**
* Get table name for link caches
*
* @param string $type Link type
* @param bool $mainTable If set to TRUE, and the type is of either 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', or 'forums_posts', then the ID is retrieved from convert_link, rather than the other link tables.
* @return string Table name
*/
protected function _getLinkTableName( string $type, bool $mainTable=FALSE ): string
{
$table = 'convert_link';
if ( $mainTable === FALSE )
{
if ( \in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map', 'forums_topics', 'forums_posts' ) ) )
{
if ( \in_array( $type, array( 'core_message_topics', 'core_message_posts', 'core_message_topic_user_map' ) ) )
{
$table = 'convert_link_pms';
}
else
{
$tableType = str_replace( 'forums_', '', $type );
$table = "convert_link_{$tableType}";
}
}
}
return $table;
}
}